[Kde-bindings] Qyoto: new features

Richard Dale richard.dale at telefonica.net
Tue Apr 10 17:25:12 UTC 2012


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.

I needed to add this implicit type conversion method to the class so that you 
can pass just an enum to a method taking a QFlags<T> argument type:

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

Here is a revised version of my example program with three methods; the first 
takes an enum, the second a uint and the third takes a QFlags<MyEnum>. I've 
added the line number of the method call as a comment along with whether it 
gets flagged with a compile error:

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
{
    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);
    }
}


Here are the compile errors for the above code:

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'
Compilation failed: 10 error(s), 0 warnings

-- Richard

> ________________________________
>  From: Richard Dale <richard.dale at telefonica.net>
> To: KDE bindings for other programming languages <kde-bindings at kde.org>
> Sent: Monday, April 9, 2012 11:35 PM
> Subject: Re: [Kde-bindings] Qyoto: new features
> 
> 
> On Monday, April 09, 2012 01:27:35 PM Dimitar Dobrev wrote:
> 
> It works because while QFlags are not technically enums, they do nothing but
> provide type safety for C++ enums, that is, practically they are enums. On
> the C# side, enums are natively type-safe so it is completely correct to
> replace QFlags with Enum.
> 
> 
> Well no I don't think that's right. If you pass 'EnumA & EnumB' as a
> argument that value isn't an enum, it is an unsigned int. The QFlags stuff
> is about constructing unsigned ints from enums that are AND'd and OR'd
> together in a type safe manner. 
> -- Richard
> _______________________________________________
> Kde-bindings mailing list
> Kde-bindings at kde.org
> https://mail.kde.org/mailman/listinfo/kde-bindings


More information about the Kde-bindings mailing list