| Topic: |
DEVELOP > c-Plus-Plus |
| User: |
"Daniel Gutson" |
| Date: |
10 Jan 2008 07:06:52 AM |
| Object: |
typesafe bitwise operations based on enums |
Hi,
I just wanted to share another library for doing type-safe bitwise
operations in C++:
http://bitwise-enum.googlecode.com
I found it useful, so hopefully it'll be for somebody else as well.
BRgds,
Daniel.
.
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 07:55:54 AM |
|
|
On Jan 10, 2:06 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
I just wanted to share another library for doing type-safe bitwise
operations in C++:
http://bitwise-enum.googlecode.com
Well, it has a number of problems. The most basic one is, of
course, that the results of or'ing or and'ing an enum aren't the
expected type. And of course, it fails for some enums.
What's wrong with just:
#define defineEnumOps( Enum, Underlying ) \
inline \
Enum operator|( Enum lhs, Enum rhs ) \
{ \
return static_cast< Enum >( \
static_cast< Underlying >( lhs ) \
| static_cast< Underlying >( rhs ) ) ; \
} \
inline \
Enum& operator|=3D( Enum& lhs, Enum rhs ) \
{ \
lhs =3D lsh | rhs ; \
return lsh ; \
} \
// and so on...
I've got code which actually parses the source file and
generates the operators directly, rather than using a #define.
But that's only because it was easy to add this to my code which
generated the enum<->string mappings---once you have the parser
and all of the information, generating the above is trivial, but
it's not worth writing all that for just the operators.
The real difficult is, of course, the underlying type. You're
probably safe using "unsigned long" in all cases, unless the
compiler also supports long long. I've found it acceptable to
require the user to provide it (defaulting to unsigned int in
the case of the code generator).
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
| User: "dizzy" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 08:23:14 AM |
|
|
James Kanze wrote:
On Jan 10, 2:06 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
I just wanted to share another library for doing type-safe bitwise
operations in C++:
http://bitwise-enum.googlecode.com
Well, it has a number of problems. The most basic one is, of
course, that the results of or'ing or and'ing an enum aren't the
expected type. And of course, it fails for some enums.
What's wrong with just:
#define defineEnumOps( Enum, Underlying ) \
inline \
Enum operator|( Enum lhs, Enum rhs ) \
{ \
return static_cast< Enum >( \
static_cast< Underlying >( lhs ) \
| static_cast< Underlying >( rhs ) ) ; \
} \
inline \
Enum& operator|=( Enum& lhs, Enum rhs ) \
{ \
lhs = lsh | rhs ; \
return lsh ; \
} \
// and so on...
<snip>
I have not read the implementation, but from the example on the site seems
that the library is intended for a different purpose then what your code is
for. It's not suposed to be used when as the result of a bitwise operation
it results the same enum type (as you said that that does not make sense
all the time) it is only made so that you can "safely" declare a function
saying that this function should take an integral value that is the result
of bitwise operations of this enum. I supose the function knowing this can
assume various things on that integral and work with it in some specific
way.
Tho I am not even sure that the library does achieve that (or if it is
possible to achieve it).
--
Dizzy
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 09:00:13 AM |
|
|
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
2) secondarily, I just use enums as the language idiom for declaring
the masks. Enums are not the central point here. Sorry if the name of
the library misleads.
Daniel.
On Jan 10, 12:23=A0pm, dizzy <di...@roedu.net> wrote:
James Kanze wrote:
On Jan 10, 2:06 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
=A0 I just wanted to share another library for doing type-safe bitwise
operations in C++:
=A0 =A0 =A0http://bitwise-enum.googlecode.com
Well, it has a number of problems. =A0The most basic one is, of
course, that the results of or'ing or and'ing an enum aren't the
expected type. =A0And of course, it fails for some enums.
What's wrong with just:
=A0 =A0 #define defineEnumOps( Enum, Underlying ) =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 inline =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\
=A0 =A0 Enum operator|( Enum lhs, Enum rhs ) =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0\
=A0 =A0 { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 =A0 =A0 return static_cast< Enum >( =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 \
=A0 =A0 =A0 =A0 =A0 =A0 static_cast< Underlying >( lhs ) =A0 =A0 =A0 =A0=
=A0 =A0\
=A0 =A0 =A0 =A0 =A0 =A0 | =A0static_cast< Underlying >( rhs ) ) ; =A0 =
=A0 \
=A0 =A0 } =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 inline =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0\
=A0 =A0 Enum& operator|=3D( Enum& lhs, Enum rhs ) =A0 =A0 =A0 =A0 =A0 =
=A0 \
=A0 =A0 { =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 =A0 =A0 lhs =3D lsh | rhs ; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 =A0 =A0 return lsh ; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0\
=A0 =A0 } =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
=A0 =A0 // =A0and so on...
<snip>
I have not read the implementation, but from the example on the site seems=
that the library is intended for a different purpose then what your code i=
s
for. It's not suposed to be used when as the result of a bitwise operation=
it results the same enum type (as you said that that does not make sense
all the time) it is only made so that you can "safely" declare a function
saying that this function should take an integral value that is the result=
of bitwise operations of this enum. I supose the function knowing this can=
assume various things on that integral and work with it in some specific
way.
Tho I am not even sure that the library does achieve that (or if it is
possible to achieve it).
--
Dizzy- Hide quoted text -
- Show quoted text -
.
|
|
|
| User: "dizzy" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 09:14:26 AM |
|
|
Daniel Gutson wrote:
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
2) secondarily, I just use enums as the language idiom for declaring
the masks. Enums are not the central point here. Sorry if the name of
the library misleads.
Maybe my understanding of "type-safe" is confusing or incomplete but I don't
understand what you mean here. Can you provide examples of code that shows
the problem that you want to solve and how using your library same code
(with small changes I supose) makes the bitwise operations "typesafe"?
(preferably not using enums if you say enums are not required)
Because IMO all bitwise operations are typesafe, they can only happen on
integral types anyways.
--
Dizzy
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 09:47:47 AM |
|
|
On Jan 10, 1:14=A0pm, dizzy <di...@roedu.net> wrote:
Daniel Gutson wrote:
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
2) secondarily, I just use enums as the language idiom for declaring
the masks. Enums are not the central point here. Sorry if the name of
the library misleads.
Maybe my understanding of "type-safe" is confusing or incomplete but I don=
't
understand what you mean here. Can you provide examples of code that shows=
the problem that you want to solve and how using your library same code
(with small changes I supose) makes the bitwise operations "typesafe"?
(preferably not using enums if you say enums are not required)
Because IMO all bitwise operations are typesafe, they can only happen on
integral types anyways.
--
Dizzy
Sure Dizzy.
The problem I try to address (and maybe this is my fault: never
stating clearly what the problem I'm addressing is), is that:
- you know enums group related constants in a strong type. I
emphasize the 'related' word here. I mean, you group a set of
constants that cannot mix with another type. For example, Colors and
Animals. Different non-overlapped sets. Then, you define functions
that either receive Colors, or Animals. But you can't pass an animal
type to a function receiving a color type. Compile-time check
warranties that.
- however, grouping related (and combinable) bitmasks is not
'typesafe': if a function receives a mask of LEDs bits, and another
receives a mask of FANs bits, no matter if you group the bits in
different enumerations, once you or them, you'll loose the type.
Example follows.
enum LED_BITS
{
BLUE_LED =3D 1<<0,
GREEN_LED =3D 1<<1,
YELLOW_LED =3D 1<<2
};
enum FANS_BITS
{
TOILETT_FAN =3D 1<<0,
LIVINGROOM_FAN =3D 1<<1,
KITCHEN_FAN =3D 1<<2
};
//and now the functions:
void set_leds(unsigned mask);
void turn_fans(unsgigned int mask);
// example:
int main() {
set_leds(GREEN_LED | KITCHEN_FAN); // oops!
}
----------
As you can see, there's no type here ensuring that you only pass a
bitwise combination of the proper type: you can mix leds with fans.
My library tries to solve this:
given the enums above, you can then:
typedef bitwise_enum<LED_BITS> Leds_mask;
typedef bitwise_enum<FAN_BITS> Fans_mask;
void set_leds(Leds_mask mask);
void turn_fans(Fans_mask mask);
int main() {
set_leds(GREEN_LED | KITCHEN_FAN); // Compiler error
}
Moreover, you can now overload functions depending on the mask.
Hope it's clearer now! And thanks Dizzy for helping me to clarify.
Daniel.
.
|
|
|
| User: "Pete Becker" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 09:56:40 AM |
|
|
On 2008-01-10 10:47:47 -0500, Daniel Gutson <danielgutson@gmail.com> said:
- however, grouping related (and combinable) bitmasks is not
'typesafe': if a function receives a mask of LEDs bits, and another
receives a mask of FANs bits, no matter if you group the bits in
different enumerations, once you or them, you'll loose the type.
That doesn't happen if you overload the bitwise operators for your
enumerated type.
My library tries to solve this:
given the enums above, you can then:
typedef bitwise_enum<LED_BITS> Leds_mask;
typedef bitwise_enum<FAN_BITS> Fans_mask;
void set_leds(Leds_mask mask);
void turn_fans(Fans_mask mask);
int main() {
set_leds(GREEN_LED | KITCHEN_FAN); // Compiler error
}
Moreover, you can now overload functions depending on the mask.
Hope it's clearer now! And thanks Dizzy for helping me to clarify.
It's clearer, but what's the advantage of this approach over defining
operator| and operator& for the enum type, and simply writing functions
that take that enum?
LED_BITS operator|(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left | right);
}
LED_BITS operator&(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left & right);
}
void set_leds(LED_BITS mask);
set_leds(GREEN_LED | KITCHEN_FAN); // compiler error
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 10:08:15 AM |
|
|
On Jan 10, 1:56=A0pm, Pete Becker <p...@versatilecoding.com> wrote:
On 2008-01-10 10:47:47 -0500, Daniel Gutson <danielgut...@gmail.com> said:=
=A0- however, grouping related (and combinable) bitmasks is not
'typesafe': if a function receives a mask of LEDs bits, and another
receives a mask of FANs bits, no matter if you group the bits in
different enumerations, once you or them, you'll loose the type.
That doesn't happen if you overload the bitwise operators for your
enumerated type.
My library tries to solve this:
given the enums above, you can then:
typedef bitwise_enum<LED_BITS> Leds_mask;
typedef bitwise_enum<FAN_BITS> Fans_mask;
void set_leds(Leds_mask mask);
void turn_fans(Fans_mask mask);
int main() {
=A0 set_leds(GREEN_LED | KITCHEN_FAN); =A0 // Compiler error
}
Moreover, you can now overload functions depending on the mask.
Hope it's clearer now! And thanks Dizzy for helping me to clarify.
It's clearer, but what's the advantage of this approach over defining
operator| and operator& for the enum type, and simply writing functions
that take that enum?
LED_BITS operator|(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left | right);
}
LED_BITS operator&(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left & right);
}
void set_leds(LED_BITS mask);
set_leds(GREEN_LED | KITCHEN_FAN); =A0 =A0 =A0// compiler error
--
=A0 Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)- Hide quoted text -
- Show quoted text -
Hi Pete.
I don't like having enum variables holding values that are not enums.
That's a hack. And I simply think that the right way to do that is a
class. Why not? It doesn't add any overhead, neither does any wrong.
Daniel.
.
|
|
|
| User: "Pete Becker" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 11:11:32 AM |
|
|
On 2008-01-10 11:08:15 -0500, Daniel Gutson <danielgutson@gmail.com> said:
I don't like having enum variables holding values that are not enums.
That's a hack.
It's an intended property of enum types. Each enum's underlying type is
required to be large enough to hold all the bits that make up every
value (that's not exactly the requirement, but you get the point),
precisely so that this kind of code will work.
And I simply think that the right way to do that is a
class. Why not? It doesn't add any overhead, neither does any wrong.
It adds mental overhead, having to remember that objects of type
LED_BITS combine to create objects of type bitwise_enum<LED_BITS>.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 11:20:49 AM |
|
|
class. Why not? It doesn't add any overhead, neither does any wrong.
It adds mental overhead, having to remember that objects of type
LED_BITS combine to create objects of type bitwise_enum<LED_BITS>.
C'mon man!! Don't be that lazy! :D
Do a typedef and you're done!!
Daniel.
.
|
|
|
| User: "Pete Becker" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 11:37:02 AM |
|
|
On 2008-01-10 12:20:49 -0500, Daniel Gutson <danielgutson@gmail.com> said:
class. Why not? It doesn't add any overhead, neither does any wrong.
It adds mental overhead, having to remember that objects of type
LED_BITS combine to create objects of type bitwise_enum<LED_BITS>.
C'mon man!! Don't be that lazy! :D
Sigh.
Do a typedef and you're done!!
Whatever you call it, it's two different names, wiht different
properties, for what should be the same thing.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
.
|
|
|
|
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
11 Jan 2008 02:59:49 AM |
|
|
On Jan 10, 5:08 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
I don't like having enum variables holding values that are not
enums. That's a hack.
It's a design feature of C++ enums. A C++ enum is not (just) an
enumerated type. I agree that the language would be clearer if
it distinguished between enumerated types and bitmap types, but
it doesn't. The keyword enum is used for both.
And I simply think that the right way to do that is a class.
Why not? It doesn't add any overhead, neither does any wrong.
If you're going that route (and I can understand it), then
wouldn't the "correct" solution be something like:
class Leds
{
public:
static Leds const redLed ;
static Leds const greenLed ;
static Leds const blueLed ;
Leds() ; // all off.
Leds( Leds const& other ) ;
Leds& operator|=3D( Leds const& other ) ;
Leds& operator&=3D( Leds const& other ) ;
freind Leds operator~( Leds const& op ) ;
private:
unsigned int myValue ;
explicit Leds( unsigned int value ) ;
} ;
Leds operator|( Leds const& lhs, Leds const& rhs ) ;
Leds operator&( Leds const& lhs, Leds const& rhs ) ;
Internally, you use bits and a bit mask on myValue, but all the
user sees is some values which he can manipulate:
Leds l1 ;
l1 |=3D redLed ;
Leds l2( l1 | greenLed ) ;
It seems a bit heavy to me, but it's certainly the most
"correct" in the abstract sense. And if you need to do it a
lot... it would take about ten minutes to write an AWK script to
generate the class, given the class name and a list of its
(constant) values.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
11 Jan 2008 02:45:04 AM |
|
|
On Jan 10, 4:56 pm, Pete Becker <p...@versatilecoding.com> wrote:
On 2008-01-10 10:47:47 -0500, Daniel Gutson <danielgut...@gmail.com> said:=
[...]
It's clearer, but what's the advantage of this approach over
defining operator| and operator& for the enum type, and simply
writing functions that take that enum?
LED_BITS operator|(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left | right);
}
LED_BITS operator&(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left & right);
}
It doesn't have infinit recursion:-).
About two times out of three, when I write the functions out
manually (rather than just invoking a macro, or using my code
generator), I write them exactly as you just did. And then
correct them after the core dump (due to stack overflow): you
need to cast at least one of the operands in the function to the
underlying type (or some integral type large enough to hold all
of the values) in order to break the recursion.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
| User: "Pete Becker" |
|
| Title: Re: typesafe bitwise operations based on enums |
11 Jan 2008 08:52:49 AM |
|
|
On 2008-01-11 03:45:04 -0500, James Kanze <james.kanze@gmail.com> said:
On Jan 10, 4:56 pm, Pete Becker <p...@versatilecoding.com> wrote:
On 2008-01-10 10:47:47 -0500, Daniel Gutson <danielgut...@gmail.com> said:
[...]
It's clearer, but what's the advantage of this approach over
defining operator| and operator& for the enum type, and simply
writing functions that take that enum?
LED_BITS operator|(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left | right);
}
LED_BITS operator&(LED_BITS left, LED_BITS right)
{
return static_cast<LED_BITS>(left & right);
}
It doesn't have infinit recursion:-).
About two times out of three, when I write the functions out
manually (rather than just invoking a macro, or using my code
generator), I write them exactly as you just did. And then
correct them after the core dump (due to stack overflow): you
need to cast at least one of the operands in the function to the
underlying type (or some integral type large enough to hold all
of the values) in order to break the recursion.
<g>
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
.
|
|
|
|
|
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 10:04:25 AM |
|
|
On Jan 10, 4:14 pm, dizzy <di...@roedu.net> wrote:
Daniel Gutson wrote:
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
2) secondarily, I just use enums as the language idiom for declaring
the masks. Enums are not the central point here. Sorry if the name of
the library misleads.
Maybe my understanding of "type-safe" is confusing or
incomplete but I don't understand what you mean here. Can you
provide examples of code that shows the problem that you want
to solve and how using your library same code (with small
changes I supose) makes the bitwise operations "typesafe"?
(preferably not using enums if you say enums are not required)
Because IMO all bitwise operations are typesafe, they can only
happen on integral types anyways.
Which isn't typesafe. Suppose you define:
enum E1 { a, b, c } ;
enum E2 { x, y, z } ;
At present, both a|b and x|y have type int, and a|x is also
legal (and has the same type). If you want to write a function
which can be called with "a|b", it can also be called with x|y
or a|z, although neither is correctly typed.
The usual solution for this is to define a set of overloaded
operators, e.g.:
E1 operator|( E1, E1 ) ;
E1 operator&( E1, E1 ) ;
etc.; having done that, the type of a|b is E1, and not int, and
it can be used anywhere an E1 is required (which an int can't
be). The definitions take a bit of typing, but are easily
handled by a macro.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 09:58:39 AM |
|
|
On Jan 10, 4:00 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
I understand that, but you don't need a separate class for that.
(Also, I was unable to get the following to compile:
#include "bitwise_enums.hpp"
#include <iostream>
enum Test { empty =3D 0, top =3D 0x80000000 } ;
void
test( bitwise_enum< Test > t )
{
std::cout << std::hex << t.value() << std:: endl ;
}
int
main()
{
test( top >> 2 ) ;
return 0 ;
}
The reason, of course, is that >> is a member, and not a free
function, as it should be, so the compiler doesn't consider
conversions for the left operand. If it compiles, I suspect
that it will give the wrong results on a lot of machines,
including mine.
And of course, if top were 0x8000000000, just about any
operation involving it would give the wrong results.
Both problems are easily fixed: make the binary operators friend
functions, and make bitwise_enum<>::_value an unsigned long (or
an unsigned long long on systems which support it). But my real
question was: is it worth it. You can achieve exactly the same
type safety with the macro I presented, and avoid the extra type
(which is confusing).
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
10 Jan 2008 06:11:04 PM |
|
|
On Jan 10, 12:58=A0pm, James Kanze <james.ka...@gmail.com> wrote:
On Jan 10, 4:00 pm, Daniel Gutson <danielgut...@gmail.com> wrote:
Duzzy, you're right: that's for a different purpose.
Please note that:
1) the MAIN goal, is to have type-safe bitwise operations
I understand that, but you don't need a separate class for that.
(Also, I was unable to get the following to compile:
=A0 =A0 #include "bitwise_enums.hpp"
=A0 =A0 #include <iostream>
=A0 =A0 enum Test { empty =3D 0, top =3D 0x80000000 } ;
=A0 =A0 void
=A0 =A0 test( bitwise_enum< Test > t )
=A0 =A0 {
=A0 =A0 =A0 =A0 std::cout << std::hex << t.value() << std:: endl ;
=A0 =A0 }
=A0 =A0 int
=A0 =A0 main()
=A0 =A0 {
=A0 =A0 =A0 =A0 test( top >> 2 ) ;
=A0 =A0 =A0 =A0 return 0 ;
=A0 =A0 }
The reason, of course, is that >> is a member, and not a free
function, as it should be, so the compiler doesn't consider
conversions for the left operand. =A0If it compiles, I suspect
that it will give the wrong results on a lot of machines,
including mine.
And of course, if top were 0x8000000000, just about any
operation involving it would give the wrong results.
Both problems are easily fixed: make the binary operators friend
functions, and make bitwise_enum<>::_value an unsigned long (or
an unsigned long long on systems which support it). =A0But my real
question was: is it worth it. =A0You can achieve exactly the same
type safety with the macro I presented, and avoid the extra type
(which is confusing).
--
James Kanze (GABI Software) =A0 =A0 =A0 =A0 =A0 =A0 email:james.ka...@gmai=
l.com
Conseils en informatique orient=E9e objet/
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0Beratung in objektorientierter Date=
nverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
Hi James, thanks for your findigs!
Two comments:
1) I will change the type of _value from int to unsigned long, or
unsigned long long when available. Thanks again for this one!
2) I'm...not 100% sure that the >> and << should be provided...
Moreover! I think to remove them from the class. Why: because by
shifting you can 'cheat' the available bitmasks. But by doing |,^, and
&, you are ensuring that what you enter is a true combination of the
available bitmasks. What do you think? I'm looking forward for your
opinion! (or anybody opinion!)
Thanks!
Daniel.
.
|
|
|
| User: "James Kanze" |
|
| Title: Re: typesafe bitwise operations based on enums |
11 Jan 2008 03:27:21 AM |
|
|
On Jan 11, 1:11 am, Daniel Gutson <danielgut...@gmail.com> wrote:
On Jan 10, 12:58 pm, James Kanze <james.ka...@gmail.com> wrote:
I understand that, but you don't need a separate class for that.
(Also, I was unable to get the following to compile:
#include "bitwise_enums.hpp"
#include <iostream>
enum Test { empty =3D 0, top =3D 0x80000000 } ;
void
test( bitwise_enum< Test > t )
{
std::cout << std::hex << t.value() << std:: endl ;
}
int
main()
{
test( top >> 2 ) ;
return 0 ;
}
Two comments:
1) I will change the type of _value from int to unsigned long, or
unsigned long long when available. Thanks again for this one!
2) I'm...not 100% sure that the >> and << should be provided...
Moreover! I think to remove them from the class. Why: because by
shifting you can 'cheat' the available bitmasks. But by doing |,^, and
&, you are ensuring that what you enter is a true combination of the
available bitmasks. What do you think? I'm looking forward for your
opinion! (or anybody opinion!)
I was wondering about that myself; if you're really trying to
implement "set of small integer" or "set of enumerated values",
then they don't belong. I chose them as an example here because
you used a (signed) int, and I think my implementations sign
extend when right shifting, which would, of course, give the
wrong results (or at least, not the results one would expect).
Using an unsigned integral type solves that, but it still leaves
open the question, what is that actual abstraction, and is
shifting appropriate for it?
The reason my example wouldn't compile, of course, is because
the operator was a member function, so the conversion Test to
bitwise_enum<Test> wasn't considered when I wrote "top >> 2".
This will be a problem for | and & as well, if they are member
functions.
If what you're really interested in is a set type, might I
suggest naming it such, and really implementing it as such:
template< typename Enum >
class SetOfEnum
{
public:
SetOfEnum()
: myValue( 0 )
{
}
/* explicit? */ SetOfEnum( Enum e )
: myValue( 1 << e )
{
}
SetOfEnum& operator|=3D( SetOfEnum const& other )
{
myValue |=3D other.myValue ;
return *this ;
}
SetOfEnum& operator|=3D( Enum other )
{
myValue |=3D 1 << other ;
return *this ;
}
// ...
private:
unsigned long myValue ;
} ;
template< typename Enum >
inline SetOfEnum< Enum >
operator|( Enum lhs, Enum rhs )
{
SetOfEnum< Enum > result( lhs ) ;
lhs |=3D rhs ;
return result ;
}
// Variants for SetOfEnum | Enum, Enum | SetOfEnum,
// and SetOfEnum | SetOfEnum...
// I prefer this and making the constructor explicit,
// if the constructor is not explicit, I think just
// Enum | Enum and SetOfEnum | SetOfEnum would suffice.
The user then declares:
enum Leds { redLed, yellowLed, greenLed, blueLed } ;
(or once the new standard is available:
enum class Leds { red, yellow, green, blue } ;
with the values named Leds::red, Leds::yellow, etc.), and then
void
f( SetOfEnums< Leds > const& leds ) ;
f( redLed | yellowLed ) ;
The name of the template justifies the use of two different
types, or at least makes it less confusing. And note that the
user isn't concerned about "bit masks"---that's implementation
technology, buried in SetOfEnum.
FWIW, you might want to take a look at my SetOfCharacter at
http://kanze.james.neuf.fr/code-en.html. (Follow the link to
the documentation for the subsystem text, then navigate to the
class SetOfCharacter.) It's a lot, lot more complicated than
what you've done, because it has to deal with the fact that char
may be signed, which means that some characters take on
different numeric values, depending on whether they're in a char
or an int (and that some people will occasionally use unsigned
char for characters as well, and that you don't want
ambiguities). And that people often think of adding and
removing elements from the set (+ and -), rather than or'ing and
and'ing. And that in the case of a set of characters, it's
often useful to be able to iterate over all of the members---one
of my original motivations for this class was to support
iterators which did so. And of course, in this case, it also
makes sense to support adding all of the characters in a string.
So it's a lot, lot more than you'd want for just an enum. But
it will give you an idea of what can be done when you go to
extremes.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34
.
|
|
|
| User: "Daniel Gutson" |
|
| Title: Re: typesafe bitwise operations based on enums |
11 Jan 2008 06:14:14 AM |
|
|
Thanks James!
Indeed, for sharing your code and ideas.
I will take the unsigned finding you did and apply it right away, and
remove >> and << family operators. However, and because of
'legacy' (...), I will preserve the name (it's a headache changing it
everywhere, since I myself use the library in many places).
There will be an acknowledgments page where you will be listed, if you
don't mind.
Thanks again!
Daniel.
.
|
|
|
|
|
|
|
|
|
|

|
Related Articles |
|
|