Re: typesafe bitwise operations based on enums
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 = 0, top = 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|=( SetOfEnum const& other )
{
myValue |= other.myValue ;
return *this ;
}
SetOfEnum& operator|=( Enum other )
{
myValue |= 1 << other ;
return *this ;
}
// ...
private:
unsigned long myValue ;
} ;
template< typename Enum >
inline SetOfEnum< Enum >
operator|( Enum lhs, Enum rhs )
{
SetOfEnum< Enum > result( lhs ) ;
lhs |= 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