[Kde-bindings] Qyoto: new features

Richard Dale richard.dale at telefonica.net
Tue Apr 10 18:00:19 UTC 2012


On Tuesday, April 10, 2012 07:32:01 PM Arno Rehn wrote:
> On 10/04/12 19:25, Richard Dale wrote:
> > On Tuesday, April 10, 2012 01:10:32 AM Dimitar Dobrev wrote:
> >> As far as I can see QFlags is used only on enums that support bitwise
> >> operation, i.e. that are defined as 1, 2, 4, 8, etc. So it is impossible
> >> to
> >> produce an invalid enum value using bitwise operations. As I said, QFlags
> >> exists only because C++ accepts an integer where an enum value is
> >> required
> >> thus violating type safety - a drawback C# does not have with its enums,
> >> that is, calling MyMethod(MyEnum myEnum) with an integer (or a short,
> >> long,
> >> etc.) fails to compile. So, if C++ enums were type-safe as C# enums,
> >> there
> >> would be no QFlags because it'd serve no purpose. And then the one
> >> difference between a working and a non-working flags enum would be that
> >> the
> >> first one is correctly (1, 2, 4...) defined. Thank you for your opinion
> >> on
> >> the matter, I appreciate it. I also appreciate your sample
> >> implementations
> >> but I want the generated API to be, besides correct, as simple as
> >> possible.
> > 
> > No, I don't think you are correct. C# has exactly the same problem as C++.
> > If you AND or OR two enums together you don't get an enum type in either
> > C++ or C#. For instance, from my example:
> > 
> > public class FlagsTest
> > {public enum MyEnum : uint {
> > 
> >          ValA = 0x0,
> >          ValB = 0x1,
> >          ValC = 0x2,
> >          ValD = 0x4
> >      
> >      }
> >      
> >      public void MyMethod(FlagsTest.MyEnum flags) {
> >      
> >        ...
> >      
> >      }
> >      
> >      ...
> > 
> > }
> > 
> > If you call MyMethod(MyEnum.ValA&  MyEnum.ValB) you will get an error in
> > C#.> 
> > If you then define MyMethod() like this:
> >     public void MyMethod(uint flags) {
> >     
> >        ...
> >      
> >      }
> > 
> > You can call MyMethod(MyEnum.ValA&  MyEnum.ValB)  OK, but you can also
> > call it with MyMethod(0x16) and it willl work fine. With my QFlags
> > example class, the first call will work, but 0x16 will give a compile
> > error.
> 
> [snip]
> 
> I think Dimitar is right here. Enums in C# are specifically designed to
> be used in the same manner that QFlags in Qt is, and you should be able
> to pass ORed and ANDed values to a method that is expecting an enum.
> Maybe the enum needs to be have the Flags attribute set for this to work:
> 
> http://docs.go-mono.com/?link=T%3aSystem.FlagsAttribute
> 
> As you can see, this really is the intended use case. Also, System.Enum
> has methods like HasFlag() which tests for a flag being set.

Ah yes you're right!  What a good language C# is..

I didn't know about the [FlagsAttribute()] attribute. Here is a final version 
of my test program using that:

using System;

public class QFlags<T> where T : struct
{
    public uint i;
    public QFlags(T flags) {
        i = Convert.ToUInt32(flags); 
    // Throw an exception if this conversion fails
    }

    public override string ToString() {
        return i.ToString();
    }

    public QFlags(uint flags) {
        i = flags; 
    // Throw an exception if this conversion fails
    }
// User-defined conversion from Digit to double

    public static implicit operator QFlags<T>(T f1) {
        uint i1 = Convert.ToUInt32(f1); 
        return new QFlags<T>(i1);
    }

    public static QFlags<T> operator|(QFlags<T> f1, T f2) { 
      uint i2 = Convert.ToUInt32(f2);    
    // Throw an exception if this conversion fails
      return new QFlags<T>(f1.i | i2);
    }    

    public static QFlags<T> operator&(QFlags<T> f1, T f2) { 
      uint i2 = Convert.ToUInt32(f2);    
    // Throw an exception if this conversion fails
      return new QFlags<T>(f1.i & i2);
    }    
}

public class FlagsTest
{
    [FlagsAttribute()] 
    public enum MyEnum : uint {
        ValA = 0x0,
        ValB = 0x1,
        ValC = 0x2,
        ValD = 0x4
    }
      
    public void MyMethod1(MyEnum flags) {
        Console.WriteLine(flags);
    }
      
    public void MyMethod2(uint flags) {
        Console.WriteLine(flags);
    }
      
    public void MyMethod3(QFlags<MyEnum> flags) {
        Console.WriteLine(flags);
    }

    static public void Main()
    {
        FlagsTest test = new FlagsTest();
        QFlags<MyEnum> f1 = new QFlags<MyEnum>(MyEnum.ValB);
        QFlags<MyEnum> f2 = f1 & MyEnum.ValC;
        /* 108 ERROR */ test.MyMethod1(f2 | MyEnum.ValD);
        /* 109 ERROR */ test.MyMethod2(f2 | MyEnum.ValD);
        /* 110 */ test.MyMethod3(f2 | MyEnum.ValD);

        /* 112 ERROR */ test.MyMethod1(0x16);
        /* 113 */ test.MyMethod2(0x16);
        /* 114 ERROR */ test.MyMethod3(0x16);

        /* 116 */ test.MyMethod1(MyEnum.ValD);
        /* 117 ERROR */ test.MyMethod2(MyEnum.ValD);
        /* 118 */ test.MyMethod3(MyEnum.ValD);

        /* 120 */ test.MyMethod1(MyEnum.ValC | DMyEnum.ValD);
        /* 121 ERROR */ test.MyMethod2(MyEnum.ValC | DMyEnum.ValD);
        /* 122 ERROR */ test.MyMethod3(MyEnum.ValC | DMyEnum.ValD);
    }
}

crawfish rdale 872% gmcs hello.cs
hello.cs(108,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod1(FlagsTest.MyEnum)' has some invalid arguments
hello.cs(91,17): (Location of the symbol related to previous error)
hello.cs(108,30): error CS1503: Argument `#1' cannot convert 
`QFlags<FlagsTest.MyEnum>' expression to type `FlagsTest.MyEnum'
hello.cs(109,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod2(uint)' has some invalid arguments
hello.cs(95,17): (Location of the symbol related to previous error)
hello.cs(109,30): error CS1503: Argument `#1' cannot convert 
`QFlags<FlagsTest.MyEnum>' expression to type `uint'
hello.cs(112,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod1(FlagsTest.MyEnum)' has some invalid arguments
hello.cs(91,17): (Location of the symbol related to previous error)
hello.cs(112,30): error CS1503: Argument `#1' cannot convert `int' expression 
to type `FlagsTest.MyEnum'
hello.cs(114,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod3(QFlags<FlagsTest.MyEnum>)' has some invalid arguments
hello.cs(99,17): (Location of the symbol related to previous error)
hello.cs(114,30): error CS1503: Argument `#1' cannot convert `int' expression 
to type `QFlags<FlagsTest.MyEnum>'
hello.cs(117,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod2(uint)' has some invalid arguments
hello.cs(95,17): (Location of the symbol related to previous error)
hello.cs(117,30): error CS1503: Argument `#1' cannot convert 
`FlagsTest.MyEnum' expression to type `uint'
hello.cs(120,48): error CS0103: The name `DMyEnum' does not exist in the 
current context
hello.cs(120,24): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod1(FlagsTest.MyEnum)' has some invalid arguments
hello.cs(91,17): (Location of the symbol related to previous error)
hello.cs(120,24): error CS1503: Argument `#1' cannot convert `object' 
expression to type `FlagsTest.MyEnum'
hello.cs(121,54): error CS0103: The name `DMyEnum' does not exist in the 
current context
hello.cs(121,30): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod2(uint)' has some invalid arguments
hello.cs(95,17): (Location of the symbol related to previous error)
hello.cs(121,30): error CS1503: Argument `#1' cannot convert `object' 
expression to type `uint'
hello.cs(122,48): error CS0103: The name `DMyEnum' does not exist in the 
current context
hello.cs(122,24): error CS1502: The best overloaded method match for 
`FlagsTest.MyMethod3(QFlags<FlagsTest.MyEnum>)' has some invalid arguments
hello.cs(99,17): (Location of the symbol related to previous error)
hello.cs(122,24): error CS1503: Argument `#1' cannot convert `object' 
expression to type `QFlags<FlagsTest.MyEnum>'
Compilation failed: 19 error(s), 0 warnings

It would be possible to fix QFlags<T> so that it doesn't give an error no line 
122, but there is no point as the C# flags attribute has the correct behaviour.

Sorrry for the noise, I think I'll go back to Ruby..

-- Richard




More information about the Kde-bindings mailing list