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

2013-08-27 Thread Eric Anholt
Francisco Jerez curroje...@riseup.net writes:

 Ian Romanick i...@freedesktop.org writes:

 On 08/26/2013 01:10 PM, Francisco Jerez wrote:
[...]
 The thing is that defining bitwise operators separately for each enum
 type, as this patch and the macro solution do, doesn't stop the compiler
  From using the corresponding built-in integer operators when it doesn't
 find a match among the user-defined ones.  That means that if we have
 two bitfield enumerants from two different disjoint types
 e.g. SEASON_OF_THE_YEAR_SUMMER and a CPU_ARCHITECTURE_I386, the
 compiler is still going to accept expressions like
 SEASON_OF_THE_YEAR_SUMMER | CPU_ARCHITECTURE_I386, which might not be
 what had been expected if the BRW_CXX_ENUM_OPS macro was used in an
 attempt to improve the code's type safety.

 This sounds insane.

 If there are no operator overloads, the compiler rejects:

 enum foo f(enum foo a, enum foo b)
 {
 return a | b;
 }


 Here both a and b would be implicitly converted to integers which are
 then or'ed together using the built-in '|' operator yielding an integer.
 Using the result as return value of your enum foo function fails
 because in C++ integers are not implicitly convertible to enum types.

 Then we add operloads:

 enum foo operator|(enum foo, enum foo);
 enum bar operator|(enum bar, enum bar);

 And now the compiler will accept:

 unsigned f(enum foo a, enum bar b)
 {
 return a | b;
 }

 That's accepted because both a and b are converted to integers as
 before, giving an integer as result, which can be implicitly converted
 to your unsigned return type just fine.  It's easy to avoid that though
 by defining a general overload:

 templatetypename T, typename S T operator|(T, S);

 for any bitfield types T and S that includes a static assertion
 within its definition to make sure that both types are indeed the same.

 In your example above the compiler would find an exact match during
 overload resolution (our operator|foo, bar(foo, bar);) so it
 wouldn't have to bother with implicit argument conversions.  The
 instantiation of that template would fail thanks to the static assertion
 requiring both T and S to be equal.

 I hope this makes the idea a bit clearer.

 That can't be right.  Am I missing something?  Or am I reinforcing my
 point about not being ready for this level of C++ ninjitsu...

This is way overcomplicated.  I think we're doing just fine with the
status quo of #defines for our bitfields with namespaced names.


pgptvgKWfB3KF.pgp
Description: PGP signature
___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


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

2013-08-26 Thread Ian Romanick

On 08/24/2013 10:41 AM, Francisco Jerez wrote:

Chad Versace chad.vers...@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:

| templatetypename T
| struct bitmask_enumeration_traits;
|
| struct __empty_bitmask {
|templatetypename T
|operator T() const {
|   bitmask_enumeration_traitsT();
|   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_traitsE {};
|

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 

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

2013-08-26 Thread Francisco Jerez
Ian Romanick i...@freedesktop.org writes:

[...]
 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.
[...]
 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.


My explanation is exactly as relevant if we stick to Chad's solution or
not, using macros doesn't save you from getting the unexpected effect I
was trying to describe -- quite the opposite, I can't think of any
simple way to work around that problem without using templates.

The thing is that defining bitwise operators separately for each enum
type, as this patch and the macro solution do, doesn't stop the compiler
From using the corresponding built-in integer operators when it doesn't
find a match among the user-defined ones.  That means that if we have
two bitfield enumerants from two different disjoint types
e.g. SEASON_OF_THE_YEAR_SUMMER and a CPU_ARCHITECTURE_I386, the
compiler is still going to accept expressions like
SEASON_OF_THE_YEAR_SUMMER | CPU_ARCHITECTURE_I386, which might not be
what had been expected if the BRW_CXX_ENUM_OPS macro was used in an
attempt to improve the code's type safety.

The template-based solution might seem somewhat obscure to the
inexperienced C++ programmer, but unlike the macro-based one it would be
semantically sound, and this is just one of many reasons why it's a good
idea for anyone dealing with C++ code to have at least some basic
knowledge on using templates -- it's the cleanest way to do static
polymorphism and generic programming in C++.

That said, I agree that it would be a bad idea to make a sudden
transition to template metaprogramming in components of our tree where
the majority of maintainers have a strong preference towards old-school
C programming, but that's no reason to reject a small, non-intrusive and
potentially useful change in the good direction a priori before having
seen any of the code...

[...]


pgpAlZKw5443E.pgp
Description: PGP signature
___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


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

2013-08-26 Thread Ian Romanick

On 08/26/2013 01:10 PM, Francisco Jerez wrote:

Ian Romanick i...@freedesktop.org writes:


[...]

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.
[...]
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.


My explanation is exactly as relevant if we stick to Chad's solution or
not, using macros doesn't save you from getting the unexpected effect I
was trying to describe -- quite the opposite, I can't think of any
simple way to work around that problem without using templates.

The thing is that defining bitwise operators separately for each enum
type, as this patch and the macro solution do, doesn't stop the compiler
 From using the corresponding built-in integer operators when it doesn't
find a match among the user-defined ones.  That means that if we have
two bitfield enumerants from two different disjoint types
e.g. SEASON_OF_THE_YEAR_SUMMER and a CPU_ARCHITECTURE_I386, the
compiler is still going to accept expressions like
SEASON_OF_THE_YEAR_SUMMER | CPU_ARCHITECTURE_I386, which might not be
what had been expected if the BRW_CXX_ENUM_OPS macro was used in an
attempt to improve the code's type safety.


This sounds insane.

If there are no operator overloads, the compiler rejects:

enum foo f(enum foo a, enum foo b)
{
return a | b;
}

Then we add operloads:

enum foo operator|(enum foo, enum foo);
enum bar operator|(enum bar, enum bar);

And now the compiler will accept:

unsigned f(enum foo a, enum bar b)
{
return a | b;
}

That can't be right.  Am I missing something?  Or am I reinforcing my 
point about not being ready for this level of C++ ninjitsu...



The template-based solution might seem somewhat obscure to the
inexperienced C++ programmer, but unlike the macro-based one it would be
semantically sound, and this is just one of many reasons why it's a good
idea for anyone dealing with C++ code to have at least some basic
knowledge on using templates -- it's the cleanest way to do static
polymorphism and generic programming in C++.

That said, I agree that it would be a bad idea to make a sudden
transition to template metaprogramming in components of our tree where
the majority of maintainers have a strong preference towards old-school
C programming, but that's no reason to reject a small, non-intrusive and
potentially useful change in the good direction a priori before having
seen any of the code...


[...]


___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


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

2013-08-26 Thread Francisco Jerez
Ian Romanick i...@freedesktop.org writes:

 On 08/26/2013 01:10 PM, Francisco Jerez wrote:
[...]
 The thing is that defining bitwise operators separately for each enum
 type, as this patch and the macro solution do, doesn't stop the compiler
  From using the corresponding built-in integer operators when it doesn't
 find a match among the user-defined ones.  That means that if we have
 two bitfield enumerants from two different disjoint types
 e.g. SEASON_OF_THE_YEAR_SUMMER and a CPU_ARCHITECTURE_I386, the
 compiler is still going to accept expressions like
 SEASON_OF_THE_YEAR_SUMMER | CPU_ARCHITECTURE_I386, which might not be
 what had been expected if the BRW_CXX_ENUM_OPS macro was used in an
 attempt to improve the code's type safety.

 This sounds insane.

 If there are no operator overloads, the compiler rejects:

 enum foo f(enum foo a, enum foo b)
 {
 return a | b;
 }


Here both a and b would be implicitly converted to integers which are
then or'ed together using the built-in '|' operator yielding an integer.
Using the result as return value of your enum foo function fails
because in C++ integers are not implicitly convertible to enum types.

 Then we add operloads:

 enum foo operator|(enum foo, enum foo);
 enum bar operator|(enum bar, enum bar);

 And now the compiler will accept:

 unsigned f(enum foo a, enum bar b)
 {
 return a | b;
 }

That's accepted because both a and b are converted to integers as
before, giving an integer as result, which can be implicitly converted
to your unsigned return type just fine.  It's easy to avoid that though
by defining a general overload:

templatetypename T, typename S T operator|(T, S);

for any bitfield types T and S that includes a static assertion
within its definition to make sure that both types are indeed the same.

In your example above the compiler would find an exact match during
overload resolution (our operator|foo, bar(foo, bar);) so it
wouldn't have to bother with implicit argument conversions.  The
instantiation of that template would fail thanks to the static assertion
requiring both T and S to be equal.

I hope this makes the idea a bit clearer.

 That can't be right.  Am I missing something?  Or am I reinforcing my
 point about not being ready for this level of C++ ninjitsu...



pgpZ0yWkFdgbH.pgp
Description: PGP signature
___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


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

2013-08-24 Thread Francisco Jerez
Chad Versace chad.vers...@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:

| templatetypename T
| struct bitmask_enumeration_traits;
| 
| struct __empty_bitmask {
|templatetypename T
|operator T() const {
|   bitmask_enumeration_traitsT();
|   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_traitsE {};
|

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.

 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.


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
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 curroje...@riseup.net

 For what it's worth, this patch is
 Reviewed-by: Chad Versace chad.vers...@linux.intel.com
 ___
 mesa-dev mailing list
 mesa-dev@lists.freedesktop.org
 http://lists.freedesktop.org/mailman/listinfo/mesa-dev


pgpjfqDaVRvuS.pgp
Description: PGP signature
___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev


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

2013-08-23 Thread Paul Berry
(From a suggestion by Francisco Jerez)

If an enum represents a bitfield of flags, e.g.:

enum E {
  A = 1,
  B = 2,
  C = 4,
  D = 8,
};

then C++ normally prohibits statements like this:

enum E x = A | B;

because A and B are implicitly converted to ints before OR-ing them,
and an int can't be stored in an enum without a type cast.  C, on the
other hand, allows an int to be implicitly converted to an enum
without casting.

In the past we've dealt with this situation by storing flag bitfields
as ints.  This avoids ugly casting at the expense of some type safety
that C++ would normally have offered (e.g. we get no warning if we
accidentally use the wrong enum type).

However, we can get the best of both worlds if we override the |
operator.  The ugly casting is confined to the operator overload, and
we still get the benefit of C++ making sure we don't use the wrong
enum type.

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.

So what do folks think?  Is it worth it?

Cc: Francisco Jerez curroje...@riseup.net
---
 src/mesa/drivers/dri/i965/brw_clip.h   |  2 +-
 src/mesa/drivers/dri/i965/brw_clip_util.c  |  2 +-
 src/mesa/drivers/dri/i965/brw_eu.h | 16 +++-
 src/mesa/drivers/dri/i965/brw_eu_emit.c|  4 ++--
 src/mesa/drivers/dri/i965/brw_sf_emit.c| 12 
 src/mesa/drivers/dri/i965/brw_vec4.h   |  2 +-
 src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp |  3 ++-
 7 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/src/mesa/drivers/dri/i965/brw_clip.h 
b/src/mesa/drivers/dri/i965/brw_clip.h
index 5af0ad3..41f5c75 100644
--- a/src/mesa/drivers/dri/i965/brw_clip.h
+++ b/src/mesa/drivers/dri/i965/brw_clip.h
@@ -173,7 +173,7 @@ void brw_clip_init_planes( struct brw_clip_compile *c );
 
 void brw_clip_emit_vue(struct brw_clip_compile *c, 
   struct brw_indirect vert,
-   unsigned flags,
+   enum brw_urb_write_flags flags,
   GLuint header);
 
 void brw_clip_kill_thread(struct brw_clip_compile *c);
diff --git a/src/mesa/drivers/dri/i965/brw_clip_util.c 
b/src/mesa/drivers/dri/i965/brw_clip_util.c
index d5c50d7..24d053e 100644
--- a/src/mesa/drivers/dri/i965/brw_clip_util.c
+++ b/src/mesa/drivers/dri/i965/brw_clip_util.c
@@ -313,7 +313,7 @@ void brw_clip_interp_vertex( struct brw_clip_compile *c,
 
 void brw_clip_emit_vue(struct brw_clip_compile *c, 
   struct brw_indirect vert,
-   unsigned flags,
+   enum brw_urb_write_flags flags,
   GLuint header)
 {
struct brw_compile *p = c-func;
diff --git a/src/mesa/drivers/dri/i965/brw_eu.h 
b/src/mesa/drivers/dri/i965/brw_eu.h
index 9053ea2..069b223 100644
--- a/src/mesa/drivers/dri/i965/brw_eu.h
+++ b/src/mesa/drivers/dri/i965/brw_eu.h
@@ -229,6 +229,8 @@ void brw_set_dp_write_message(struct brw_compile *p,
  GLuint send_commit_msg);
 
 enum brw_urb_write_flags {
+   BRW_URB_WRITE_NO_FLAGS = 0,
+
/**
 * Causes a new URB entry to be allocated, and its address stored in the
 * destination register (gen  7).
@@ -271,11 +273,23 @@ enum brw_urb_write_flags {
   BRW_URB_WRITE_ALLOCATE | BRW_URB_WRITE_COMPLETE,
 };
 
+#ifdef __cplusplus
+/**
+ * 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
+
 void brw_urb_WRITE(struct brw_compile *p,
   struct brw_reg dest,
   GLuint msg_reg_nr,
   struct brw_reg src0,
-   unsigned flags,
+   enum brw_urb_write_flags flags,
   GLuint msg_length,
   GLuint response_length,
   GLuint offset,
diff --git a/src/mesa/drivers/dri/i965/brw_eu_emit.c 
b/src/mesa/drivers/dri/i965/brw_eu_emit.c
index b55b57e..ecf8597 100644
--- a/src/mesa/drivers/dri/i965/brw_eu_emit.c
+++ b/src/mesa/drivers/dri/i965/brw_eu_emit.c
@@ -515,7 +515,7 @@ static void brw_set_ff_sync_message(struct brw_compile *p,
 
 static void brw_set_urb_message( struct brw_compile *p,
 struct brw_instruction *insn,
- unsigned flags,
+ enum brw_urb_write_flags flags,
 GLuint msg_length,
 GLuint response_length,
 GLuint offset,
@@ -2213,7 +2213,7 @@ void brw_urb_WRITE(struct brw_compile *p,
   struct brw_reg dest,
   GLuint msg_reg_nr,
   struct brw_reg src0,
-   unsigned flags,
+

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

2013-08-23 Thread Mark Mueller
This is a nice improvement over the explicit cast, which is how I've always
done it in the past - which is the ugly part of an otherwise great method
for flags. Also I use  a lot with enum for clearing bits.


On Fri, Aug 23, 2013 at 3:18 PM, Paul Berry stereotype...@gmail.com wrote:

 (From a suggestion by Francisco Jerez)

 If an enum represents a bitfield of flags, e.g.:

 enum E {
   A = 1,
   B = 2,
   C = 4,
   D = 8,
 };

 then C++ normally prohibits statements like this:

 enum E x = A | B;

 because A and B are implicitly converted to ints before OR-ing them,
 and an int can't be stored in an enum without a type cast.  C, on the
 other hand, allows an int to be implicitly converted to an enum
 without casting.

 In the past we've dealt with this situation by storing flag bitfields
 as ints.  This avoids ugly casting at the expense of some type safety
 that C++ would normally have offered (e.g. we get no warning if we
 accidentally use the wrong enum type).

 However, we can get the best of both worlds if we override the |
 operator.  The ugly casting is confined to the operator overload, and
 we still get the benefit of C++ making sure we don't use the wrong
 enum type.

 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.

 So what do folks think?  Is it worth it?

 Cc: Francisco Jerez curroje...@riseup.net
 ---
  src/mesa/drivers/dri/i965/brw_clip.h   |  2 +-
  src/mesa/drivers/dri/i965/brw_clip_util.c  |  2 +-
  src/mesa/drivers/dri/i965/brw_eu.h | 16 +++-
  src/mesa/drivers/dri/i965/brw_eu_emit.c|  4 ++--
  src/mesa/drivers/dri/i965/brw_sf_emit.c| 12 
  src/mesa/drivers/dri/i965/brw_vec4.h   |  2 +-
  src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp |  3 ++-
  7 files changed, 30 insertions(+), 11 deletions(-)

 diff --git a/src/mesa/drivers/dri/i965/brw_clip.h
 b/src/mesa/drivers/dri/i965/brw_clip.h
 index 5af0ad3..41f5c75 100644
 --- a/src/mesa/drivers/dri/i965/brw_clip.h
 +++ b/src/mesa/drivers/dri/i965/brw_clip.h
 @@ -173,7 +173,7 @@ void brw_clip_init_planes( struct brw_clip_compile *c
 );

  void brw_clip_emit_vue(struct brw_clip_compile *c,
struct brw_indirect vert,
 -   unsigned flags,
 +   enum brw_urb_write_flags flags,
GLuint header);

  void brw_clip_kill_thread(struct brw_clip_compile *c);
 diff --git a/src/mesa/drivers/dri/i965/brw_clip_util.c
 b/src/mesa/drivers/dri/i965/brw_clip_util.c
 index d5c50d7..24d053e 100644
 --- a/src/mesa/drivers/dri/i965/brw_clip_util.c
 +++ b/src/mesa/drivers/dri/i965/brw_clip_util.c
 @@ -313,7 +313,7 @@ void brw_clip_interp_vertex( struct brw_clip_compile
 *c,

  void brw_clip_emit_vue(struct brw_clip_compile *c,
struct brw_indirect vert,
 -   unsigned flags,
 +   enum brw_urb_write_flags flags,
GLuint header)
  {
 struct brw_compile *p = c-func;
 diff --git a/src/mesa/drivers/dri/i965/brw_eu.h
 b/src/mesa/drivers/dri/i965/brw_eu.h
 index 9053ea2..069b223 100644
 --- a/src/mesa/drivers/dri/i965/brw_eu.h
 +++ b/src/mesa/drivers/dri/i965/brw_eu.h
 @@ -229,6 +229,8 @@ void brw_set_dp_write_message(struct brw_compile *p,
   GLuint send_commit_msg);

  enum brw_urb_write_flags {
 +   BRW_URB_WRITE_NO_FLAGS = 0,
 +
 /**
  * Causes a new URB entry to be allocated, and its address stored in
 the
  * destination register (gen  7).
 @@ -271,11 +273,23 @@ enum brw_urb_write_flags {
BRW_URB_WRITE_ALLOCATE | BRW_URB_WRITE_COMPLETE,
  };

 +#ifdef __cplusplus
 +/**
 + * 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
 +
  void brw_urb_WRITE(struct brw_compile *p,
struct brw_reg dest,
GLuint msg_reg_nr,
struct brw_reg src0,
 -   unsigned flags,
 +   enum brw_urb_write_flags flags,
GLuint msg_length,
GLuint response_length,
GLuint offset,
 diff --git a/src/mesa/drivers/dri/i965/brw_eu_emit.c
 b/src/mesa/drivers/dri/i965/brw_eu_emit.c
 index b55b57e..ecf8597 100644
 --- a/src/mesa/drivers/dri/i965/brw_eu_emit.c
 +++ b/src/mesa/drivers/dri/i965/brw_eu_emit.c
 @@ -515,7 +515,7 @@ static void brw_set_ff_sync_message(struct brw_compile
 *p,

  static void brw_set_urb_message( struct brw_compile *p,
  struct brw_instruction *insn,
 - unsigned flags,
 + enum brw_urb_write_flags flags,
 

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

2013-08-23 Thread Chad Versace

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.

Disadvantage (b) can be made painless with the macro I discuss below.



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.

For what it's worth, this patch is
Reviewed-by: Chad Versace chad.vers...@linux.intel.com
___
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev