1. 44

  2. 5

    Isn’t this generalized by enumerations?

    I really liked the contrast of the original API vs the one using chained methods and the bitmasks. Drives the message home right away.

    1. 2

      Yes, and C# has an attribute called ‘Flags’ you can apply so that tooling can know these values are meant to be ORed together and help out.

      Example from Microsoft:

      public enum Days
          None      = 0b_0000_0000,
          Monday    = 0b_0000_0001,
          Tuesday   = 0b_0000_0010,
          Wednesday = 0b_0000_0100,
          Thursday  = 0b_0000_1000,
          Friday    = 0b_0001_0000,
          Saturday  = 0b_0010_0000,
          Sunday    = 0b_0100_0000,
          Weekend   = Saturday | Sunday
      1. 1

        Is it? Bitmasks can be combined with bitwise or, I’m not aware of similar enum combinations in the implementations I’m familiar with.

        1. 1

          C lets you do that with enums; from https://en.cppreference.com/w/c/language/enum :

          Enumerated types are integer types, and as such can be used anywhere other integer types can, including in implicit conversions and arithmetic operators.

           enum { ONE = 1, TWO } e;
           long n = ONE; // promotion
           double d = ONE; // conversion
           e = 1.2; // conversion, e is now ONE
           e = e + 1; // e is now TWO

          This works in clang 12:

          #include <stdio.h>
          enum asdf { A = 1, B = 2, C = 4 };
          int main() {
          	enum asdf x = A;
          	x = B | C;
          	printf("%d\n", (int) x);
          > clang --version
          Apple clang version 12.0.0 (clang-1200.0.26.2)
          Target: x86_64-apple-darwin20.1.0
          Thread model: posix
          InstalledDir: /Library/Developer/CommandLineTools/usr/bin
          > clang -o bitmask_enum bitmask_enum.c
          > ./bitmask_enum
          1. 2

            The lack of type- and range-checking can be considered a drawback. B | C isn’t a valid asdf value since the declaration has no item equal to 6; its type is actually int. C++ is stricter and won’t let you assign that back to an asdf variable without a typecast.

            This makes using enums as bit-sets annoying in C++-compatible code. Apple’s frameworks work around this by making the bit-set type actually a typedef for unsigned, not the enum type itself.

            1. 1

              The lack of type- and range-checking can be considered a drawback.

              So much of typing in C can and should be considered a drawback, and I wouldn’t shed a tear if software written in C went off into the sunset.

              Swapping the stdio.h to cstdio and running the same file through clang++ does in fact error, which, yeah makes sense :

              clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
              bitmask_enum.c:7:8: error: assigning to 'enum asdf' from incompatible type
                      x = B | C;
              1 error generated.

              And C++ is kind of ridiculous about what you gotta do to use a scoped enumeration (c.f. https://en.cppreference.com/w/cpp/language/enum ) as content for a bitmask:

              #include <cstdio>
              enum class jkl { A = 1, B = 2, C = 4};
              int main() {
              	enum jkl x = jkl::A;
              	int f = (int)jkl::B | (int)jkl::C;
              	printf("%d\n", f);

              In conclusion, ¯\_(ツ)_/¯

              1. 3

                It’s another case where C++ gave us something good (enum classes) but failed to add language support for making it pleasant to use. (See also: variants; functors and iterators 2003-2010; and apparently async/await in C++20.)

                You can fix this, but it requires writing a bunch of operator overloads on your enum class to ad the necessary type-casts. Not rocket science, but why was this not added to the standard library?

            2. 1

              works in clang 12

              It should work in any conformant c compiler.

        2. 3

          I kind of like the 0bxxxxxx format for defining the bit masks (since you can see which bit is on) but you can also do them like so (a bit more concise?):

          const (
              BITONE int64 = 1 << (1*iota)  
          1. 5

            Yeah, in the actual code it uses iota; there are quite a few differences between the actual implementation and the code I used in this explainer, as I felt it would be easier (especially for people not familiar with Go, since iota is fairly unique to Go).

            It’s mentioned in the article too, together with some code which uses iota:

            In many of the above examples I used binary literals for the constants, and this seemed the best way to communicate how it all works for this article. This isn’t necessarily the best or easiest way to write things in actual code, especially not for such large numbers. In the actual code it looks like:

            1. 1

              Ah, thanks! I skimmed the article and code since I agree bitmasks are nicer for somethings (my Dbg package uses them), my apologies.

            2. [Comment from banned user removed]

              1. 2

                Who claims that it’s built for dumb people? I’ve seen the quote be Rob Pike about Go being designed for new googlers that are incapable of understanding a “brilliant” language, but I’ve never heard anyone say that it was built for dumb people.

                PS iota is a bit too magical, but it’s relatively benign.

                1. 1

                  Bitmasks are common, but even if you don’t have experience with them, that’s all implementation detail. The library’s API can be used without understanding the underlying mechanics, and I do think this is a simpler and more readable API than the color’s.

              2. 3

                Both Swift and Nim allow bit-masks to be declared and used as sets, which is very convenient.

                In fact, Nim’s built-in set type is a bit-set. This frequently confuses newbies who are trying to use it as a more typical hashtable-based set, or who don’t understand why the compiler won’t let them have a set[int64] (it’s because it would be 2^61 bytes large!)

                1. 3

                  At work we use this in our coffee machines, just wrote about it here this morning : https://lobste.rs/s/dykm2t/i_ve_packaged_up_gnash_as_snap_for_modern#c_9v4bkv

                  1. 2

                    Honest question: is a bit mask “flags” type argument, really not a common concept for a lot of programmers?

                    1. 1

                      You’d have to run some sort of (representative) survey to be sure, but I suspect for a lot of people it’s not really. People have heard of it, and may have used libraries which use it, but I have the impression that not many have actually implemented anything with it.

                      In my almost 20 years of programming I’ve never done it before, and I have never seen any of my coworkers implement it too. It’s probably more common in some environment such as C and C++.

                      1. 2

                        I see.

                        I write a pretty decent amount of PHP - it’s exposed to userland developers pretty early on, which I guess partly comes from a lot of the low level stuff being thin wrappers around C libs, and the natural influence that gave to engine stuff (e.g. setting the error reporting level is probably the most common one php dev’s would experience).

                        Im sure it’s also implemented by many fewer than actually understand what it is when using it, but I feel like it’s not that uncommon for php libs.

                        1. 1

                          Yeah, you use it in PHP in various places, but I can’t recall every seeing a PHP implementation (I’m sure they exist, but it’s not common). Using something like this is essentially just a matter of remembering syntax (mostly |).

                          1. 1

                            Maybe I’m just biassed and think it’s more common than it is. I use it semi-regularly in my own libs/client work.

                    2. 1

                      This is cool!