Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

What A Wonderful World: Bitmasks!

by Revelation (Deacon)
on Dec 08, 2002 at 07:07 UTC ( #218342=perlmeditation: print w/replies, xml ) Need Help??

I'd just like to propose that developers of larger packages use bitmasks instead of lots of 0,1 (true or false) variables. This may be the standard for enlightened coders, or it might be a foolish notion by me, so I decided to ask you guys to assess the validity of my reasoning.

My reasoning for this is that Bitmasks can:
Save space, if you are shifting a lot. ( No more  my($is_alive,$is_cool,$is_a_smurf) = @_; ) However, this does not seem to be a valid purpose in and of itself, since most maintenance programmers may not come with knowledge of bitmasks, but will definitely understand true and false values.

Therefore, I reduce code, but the cons outnumber the pros, right? But no! What if I want to add a value to my subroutine checks? Maintenance, and code changes become a big hassle. I'll have to add that extra value in every subroutine that uses it. So how do we solve this? You could use a hash with all the values! A hash is especially a good fit if you are using an OO construct, where a HoH and perldsc types of things are right up your alley.

But I believe that bitmasks are still more helpful than hashes with 0,1 values, because they help you organize your packages, by allowing you to put constants somewhere else. I like to remind myself, and force myself to use the same constructs everywhere in my code. Code consistency means code clarity for a maintenance programmer, and it means simplicity for me. (My reasoning for using strict.) The same way I like to remind myself of the constructs I'm using, and be able to have one persistent variable that I can pass around to subroutines. Why us a hash, when you can use a scalar? And so... I use Bitmasks.

Does this sound valid to you, or am I just being an idiot? ( A why would be helpful) Is there somewhere else where Bitmasks should be used, instead? If you use Bitmasks for a different reason in this case, what is it?
In addition, here's a quick example of where I think Bitmasks are helpful: Pretend this is a random module encapsulated by a greater package. Instead of:
sub test_stuff { my($is_alive,$is_cool,$is_a_smurf,$is_a_moose) = @_; if ( $is_alive ) { print "It's alive!"; } elsif ( $is_cool || $is_a_smurf ) { print "It's either cool xor a smurf"; } elsif ( $is_a_moose ) { print "It's a moose"; } else { print "It's something strange :("; } } sub test_more_stuff { my($is_a_cow,$is_a_pow,$is_a_how,$is_a_now) = @_; if ( $is_a_cow ) { print "It's alive!"; } elsif { etc... etc...} }
package main; use Rev::Bitmasks qw(ALIVE COOL SMURF MOOSE COW POW HOW BOW); sub test_stuff { my $persistent = shift; if ( $persistent & ALIVE ) { print "It's alive!"; } elsif ( $persistent & (COOL | SMURF) ) { print "It's either cool xor a smurf"; } elsif ( $persistent & MOOSE ) { print "It's a moose"; } else { print "It's something strange :("; } } sub test_more_stuff { my $persistent = shift; if ( $persistent & COW ) { print "It's alive!"; } elsif { etc... etc...} } package Rev::Bitmasks; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(ALIVE COOL SMURF MOOSE COW POW HOW BOW); sub ALIVE () { 1; }; sub COOL () { 2; }; sub SMURF () { 4; }; sub MOOSE () { 8; }; sub COW () { 16; }; sub POW () { 32; }; sub HOW () { 64; }; sub BOW () { 128; };

Replies are listed 'Best First'.
Re: What A Wonderful World: Bitmasks!
by chromatic (Archbishop) on Dec 08, 2002 at 07:35 UTC

    Some cons:

    • You're polluting namespaces with constants
    • You can only express boolean conditions without calculating them
    • Every data structure has to have a slot for all possible conditions
    • Any code that deals with conditions has to know about all possible conditions
    • You're separating the meaning of a thing from the description of the thing
    • You're requiring *more* syntax, and it's punctuation
    • You're dictating the implementation of future potential data structures

    They're a tool, like everything else, and have their uses in certain circumstances.

Re: What A Wonderful World: Bitmasks!
by theorbtwo (Prior) on Dec 08, 2002 at 08:11 UTC

    More cons, in addition to chromatic's: You can only have 32 flags without requiring two flag words (64 bits on some platforms, with some compilation flags and versions of perl -- a maintance nightmare). You could accidently use the wrong flags for the puticular function you're using, and it'll simply do the wrong thing silently -- no error, no possible way to catch it.

    Bitmasks absolutely have uses, don't get me wrong -- when you're in a C struct, and want to have room for expansion without making the struct larger. When space is at a premium -- if you were going to make an array of thoustands of items, using bitmasks instead of individual flag keys in a hash would make sense. But for function args, I'd use a hash of flags.

    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: What A Wonderful World: Bitmasks!
by dws (Chancellor) on Dec 08, 2002 at 08:41 UTC
    At a conceptual level, packaging a set of boolean values into a bitmask does seem to be equivalent to packaging them in a hash, and at an implementation level, it does seem like a way to save space. But I think it's a dangerous way to proceed, for several reasons:

    1. It's too easy to do hard-to-detect damage other bits as a side-effect of making a change to one bit.

    2. The steps you might take to avoid collateral damage have the effect of burdening the application. Compare

    $args{FOO) = 1; $args{BAR} = 0;
    setbit($args, FOO}; clearbit($args, BAR);

    3. Method signatures (such as they are in Perl) lose expressive power when arguments are packed together. It gets harder to get a sense of what a chunk of code is doing when you can't quickly and easily tell what a subroutine or method is taking as input (or producing as output). Conceptually, you're widening the method signature to include all of the values packed in to the bit pack, whether they're used or not.

      4. It's nowhere near as easy for the caller to dynamically construct a bitmask as it is to dynamically construct a list of booleans. You will spend a lot of time doing things like
      my $param = 0; $param |= FOO if $foo > 0; $param |= BAR if $bar != 1; $param |= BAZ if $baz > $foo; quux($param);
      where you would otherwise just write quux($foo > 0, $bar != 1, $baz > $foo);
      Ok, it might work (I didn't test, might need an extra dozen parens) to say something like quux( ($foo > 0 && FOO) | ($bar != 1 && BAR) | ($baz > $foo && BAZ) );
      but I don't think I need to comment on that.

      Makeshifts last the longest.

Re: What A Wonderful World: Bitmasks!
by jkahn (Friar) on Dec 08, 2002 at 21:27 UTC
    In addition to the excellent responses from chromatic, theorbtwo and dws, I'd like to point out one further potential problem.

    I frequently find that it's valuable -- at least in initialization -- to have three values for my user parameters -- true, false, and undefined, so I can tell the difference between never set and false.

    Bitmasks default to 0 (at least, that is the usual convention), and from inside my initializer, I can't tell whether the user neglected to set (e.g.) COW or if s/he explictly set !COW. So distinguishing undef in this three-valued system allows me to detect the neglect case and set a default or carp appropriately.

    Hmm... on reflection, I wonder how many other languages provide this useful distinction? C certainly doesn't, and it's a recurring PITA in my work there. I find Perl's distinction in this domain (a built in trinary Boolean value, if you will) to be one of its truly endearing features.

    Just my $0.02 US.

      I agree about the undef point. A similar idea is taking advantage of perls flexible parameter passing, such as checking to see how many args were passed.

      --- demerphq
      my friends call me, usually because I'm late....

Re: What A Wonderful World: Bitmasks!
by premchai21 (Curate) on Dec 08, 2002 at 16:46 UTC
Re: What A Wonderful World: Bitmasks!
by BUU (Prior) on Dec 08, 2002 at 08:10 UTC
    A slightly differen response. Would you mind giving a very simple explanation of how exactly bitmasks work? (Along with possibly what & and | do..)

      Here is how it works and some example of how you use AND & OR | and XOR ^ with bit masks. AND is true is both bits AND-ED together are true, otherwise it is false. OR is true if either of the bits OR-ED together are true otherwise it is false. XOR (Exclusive OR )is true if only one of bits XOR-ED together is true. It is false if both bits are true or both bits are false. Although XOR might seem a little odd it has some particularly interesting properties.

        Good node but woefully incomplete! How could you forget bitwise negation? It's really rather useful with bitmasks. For instance, $flags &= ~$foo_flag is a common idiom for flipping a bit off.

        I'll pick a nit while I'm at it. Throughout your node you refer to "binary AND", "binary OR", and "binary XOR". Strictly speaking, you mean "bitwise" rather than "binary." A binary operator is an operator that takes two operands. Both logical and bitwise AND are binary operators. For contrast, consider unary operators such as numerical, logical, and bitwise negation which take one operand as well as the trinary operator (?:) which takes three.

        Just the same, ++ for the otherwise thorough explanation.

        "My two cents aren't worth a dime.";
        Excellent node. I completely forgot to cover the operators.

        One thing id like to add however is that your comment about XOR being the basis of simple encryption, while being true, should not be emulated. XOR based cyphers are particularly vulnerable to being cracked. Also, the most common use of XOR is right in front of everybodys eyes: graphics.

        --- demerphq
        my friends call me, usually because I'm late....

      A bitmaks and binary tests are just the idea of compressing a bunch of small width data (usually booleans, ie 1 bit values) into a byte. So for instance we might use a bit field to represent which external walls of a rectangle in a spreadsheet get colored in and which internal lines get colored in:
      Bit 7 : reserved 6 : reserved 5 : inside horiz 4 : inside vert 3 : bottom 2 : top 1 : right 0 : left
      So a value of 255 corresponds to all sides being colored in. A value of 3 means just the left and right, a value of 5 is the top and left etc...

      --- demerphq
      my friends call me, usually because I'm late....

Re: What A Wonderful World: Bitmasks!
by Abigail-II (Bishop) on Dec 09, 2002 at 17:44 UTC
    I wouldn't overrate the uses of bitmasks. Sure, they save memory, bit in situations where memory is that important, you probably shouldn't have used Perl in the first place. Furthermore, bitmasks aren't completely portable; the number of bits in an integer isn't the same everywhere.

    Also, the bitwise operators aren't playing the "there's no difference between a string and a number" game in Perl very well:

    perl -wle 'print +("30" | "50") == (30 | 50) ? "Yes" : "No"' No

    Forgetting that commandline arguments, or regex captures are strings, even if they only contain numbers will bite you sooner or later. And no use of strict or warnings is going to catch that for you - Perl will just think you are doing a string-wise AND or OR.

    If I work with bitfields, I tend to prefer bitstrings, and get the individual bits out of them with vec. This will save memory as well, is more flexible (instead of 2 values, you can opt for 4, 8, 16, etc as well), and doesn't have the idiosyncrasies you have with the bitwise operators.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://218342]
Approved by mikeirw
Front-paged by data64
[Lady_Aleena]: Hello. Why is this dying at -exec: my @music_times = qx(find ~/Music/Albums/ -type f -iname '*.mp3' -exec {} \;); with the error find: missing argument to `-exec'
[Lady_Aleena]: ACK! my @music_times = qx(find ~/Music/Albums/ -type f -iname '*.mp3' -exec mp3info -p "%S\n" {} \;);
[marioroy]: :)
marioroy adds oatmeal cookies to the platter on the sideboard.
marioroy adds oatmeal raisin cookies to the platter on the sideboard.

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2017-04-23 09:08 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (430 votes). Check out past polls.