typesafe bitwise operations based on enums

Discussion in 'C++' started by Daniel Gutson, Jan 10, 2008.

  1. 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.
     
    Daniel Gutson, Jan 10, 2008
    #1
    1. Advertising

  2. Daniel Gutson

    James Kanze Guest

    On Jan 10, 2:06 pm, Daniel Gutson <> 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...

    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:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jan 10, 2008
    #2
    1. Advertising

  3. Daniel Gutson

    dizzy Guest

    James Kanze wrote:

    > On Jan 10, 2:06 pm, Daniel Gutson <> 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
     
    dizzy, Jan 10, 2008
    #3
  4. Daniel Gutson

    Pete Becker Guest

    On 2008-01-10 10:47:47 -0500, Daniel Gutson <> 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)
     
    Pete Becker, Jan 10, 2008
    #4
  5. On Jan 10, 1:56 pm, Pete Becker <> wrote:
    > On 2008-01-10 10:47:47 -0500, Daniel Gutson <> 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)- 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.
     
    Daniel Gutson, Jan 10, 2008
    #5
  6. Daniel Gutson

    Pete Becker Guest

    On 2008-01-10 11:08:15 -0500, Daniel Gutson <> 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)
     
    Pete Becker, Jan 10, 2008
    #6
  7. > > 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.
     
    Daniel Gutson, Jan 10, 2008
    #7
  8. Daniel Gutson

    Pete Becker Guest

    On 2008-01-10 12:20:49 -0500, Daniel Gutson <> 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)
     
    Pete Becker, Jan 10, 2008
    #8
  9. Daniel Gutson

    James Kanze Guest

    On Jan 10, 4:56 pm, Pete Becker <> wrote:
    > On 2008-01-10 10:47:47 -0500, Daniel Gutson <> 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:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jan 11, 2008
    #9
  10. Daniel Gutson

    James Kanze Guest

    On Jan 10, 5:08 pm, Daniel Gutson <> 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|=( Leds const& other ) ;
    Leds& operator&=( 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 |= 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:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jan 11, 2008
    #10
  11. Daniel Gutson

    Pete Becker Guest

    On 2008-01-11 03:45:04 -0500, James Kanze <> said:

    > On Jan 10, 4:56 pm, Pete Becker <> wrote:
    >> On 2008-01-10 10:47:47 -0500, Daniel Gutson <> 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)
     
    Pete Becker, Jan 11, 2008
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Rhino
    Replies:
    8
    Views:
    395
    Thomas Hawtin
    Dec 15, 2005
  2. z-man
    Replies:
    6
    Views:
    3,031
    z-man
    Oct 3, 2006
  3. =?utf-8?b?QXNiasO4cm4gU8OmYsO4?=

    Enums without identifier, enums and typedef

    =?utf-8?b?QXNiasO4cm4gU8OmYsO4?=, Jan 19, 2007, in forum: C Programming
    Replies:
    10
    Views:
    1,138
    Keith Thompson
    Jan 20, 2007
  4. Daniel Gutson
    Replies:
    3
    Views:
    540
    James Kanze
    Jan 10, 2008
  5. James Kanze
    Replies:
    3
    Views:
    390
    Daniel Gutson
    Jan 11, 2008
Loading...

Share This Page