[Mesa-dev] [RFC PATCH] i965: Allow C++ type safety in the use of enum brw_urb_write_flags.

Ian Romanick idr at freedesktop.org
Mon Aug 26 11:23:38 PDT 2013


On 08/24/2013 10:41 AM, Francisco Jerez wrote:
> Chad Versace <chad.versace at linux.intel.com> writes:
>
>> On 08/23/2013 02:18 PM, Paul Berry wrote:
>>
>>> The disadvantages are that (a) we need an explicit enum value for 0,
>>> and (b) we can't use related operators like |= unless we define
>>> additional overloads.
>>
>> Disadvantage (a) is trivial, not really a problem.
>>
> I agree, it's not a real disadvantage, you can always define an "empty"
> enumerant that evaluates to zero.  If that seems inconvenient or ugly we
> can define a global constant that may be implicitly converted to any
> bitmask-like enumeration type evaluating to zero, as in:
>
> | template<typename T>
> | struct bitmask_enumeration_traits;
> |
> | struct __empty_bitmask {
> |    template<typename T>
> |    operator T() const {
> |       bitmask_enumeration_traits<T>();
> |       return T();
> |    }
> |
> |    operator unsigned() const {
> |       return 0;
> |    }
> | };
> |
> | const __empty_bitmask empty;
>
> The "bitmask_numeration_traits" structure makes sure that the
> polymorphic conversion operator won't be instantiated for non-bitmask
> types.  The enum declaration would look like:
>
> | enum E {
> |  A = 1,
> |  B = 2,
> |  C = 4,
> |  D = 8
> | };
> |
> | template<>
> | struct bitmask_enumeration_traits<E> {};
> |
>
> Actually, it *is* possible to arrange for the literal 0 to be
> implicitly converted to our bitmask enum types in a type-safe way by
> exploiting the fact that the language allows it to be implicitly
> converted to any pointer type (while implicit conversion of any other
> integral expression to a pointer type is disallowed).  Though I believe
> it would involve using an actual object instead of plain enums because
> it's not possible to define a conversion constructor for an enum type.
>
>> Disadvantage (b) can be made painless with the macro I discuss below.
>>
>>
> IMHO it would be nicer to define generic templates implementing
> overloads for all bitwise operators.  They would have to reference the
> bitmask_enumeration_traits structure so they would be discarded for
> non-bitmask types.
>
> Aside from being nicer and safer it would have two clear advantages.
> First, if we decide to use the "__empty_bitmask" type defined above, we
> wouldn't have to keep around three different overloads for each binary
> operator (T op T, empty op T, T op empty), we'd just define a general
> one 'T op S' that would be discarded by the SFINAE rule for incompatible
> T and S.
>
> Second, we could arrange for the expression 'FOO op BAR' (with 'FOO' and
> 'BAR' enumerants from different incompatible bitmask types) to be
> rejected by the compiler by means of a static assertion in the
> definition of 'T op S'.  If we use the macro solution below the compiler
> will accept that expression by downgrading both T and S to integers and
> then applying the built-in definition of 'op'.  Though it would still
> refuse to assign the result to a variable of any of both types *if* the
> user is doing that.

As a non-C++ programmer, that explanation gave me a headache.  I don't 
think this project is ready yet for its developers to need that level of 
knowledge of the C++ type system.

I can immediately understand Chad's macro, and I can also (nearly 
immediately) understand that it's probably not the "C++ way."

>>> So what do folks think?  Is it worth it?
>>
>>
>> Yes, I think it's worth it. The code becomes more readable and
>> more type safe, as evidenced by the diff lines like this:
>>> -                       unsigned flags,
>>> +                       enum brw_urb_write_flags flags,
>>
>> If we continue to do this to other enums, then we should probably
>> define a convenience macro to define all the needed overloaded
>> bit operators. Like this:
>>
>>    #define BRW_CXX_ENUM_OPS(type_name) \
>>        static inline type_name \
>>        operator|(type_name x, type_name y) \
>>        { \
>>           return (type_name) ((int) x | (int) y); \
>>        } \
>>        \
>>        static inline type_name \
>>        operator&(........) \
>>        ........ and more operators
>>
>>
>>> +/**
>>> + * Allow brw_urb_write_flags enums to be ORed together (normally C++ wouldn't
>>> + * allow this without a type cast).
>>> + */
>>> +inline enum brw_urb_write_flags
>>> +operator|(enum brw_urb_write_flags x, enum brw_urb_write_flags y)
>>> +{
>>> +   return (enum brw_urb_write_flags) ((int) x | (int) y);
>>> +}
>>> +#endif
>>
>> I think the comment is distracting rather than helpful. There is no need for C++
>> code to apologize for being C++ code.

You keep forgetting that a number of the people on this project are not 
C++ programmers.  Every one of them will ask, "Why is this necessary?" 
Comments are for communicating with humans.

> I agree, the comment seems redundant to me (as well as using the 'enum'
> keyword before enum names, though that's just a matter of taste).  In a

Again, this communicates to other humans what the thing is.  If I just 
see "foo x;" as a declaration, I don't know what kind of a thing (class, 
POD struct, enum, typedef, etc.) x is.  While the compiler doesn't care, 
humans do.

> general definition you might want to use the static_cast<> operator
> instead of a c-style cast, to make clear you're only interested in
> "safe" integer-to-enum casts.
>
> In any case this seems like a good thing to do, already in this state,
> Reviewed-by: Francisco Jerez <currojerez at riseup.net>
>
>> For what it's worth, this patch is
>> Reviewed-by: Chad Versace <chad.versace at linux.intel.com>
>> _______________________________________________
>> mesa-dev mailing list
>> mesa-dev at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/mesa-dev



More information about the mesa-dev mailing list