language-agnosticcode-generationbit-fieldsbitflags

How to pick bitflag values?


I have a set of options, some orthogonal (can be combined in any combination), some exclusive (only one from the set is allowed), and need to pick a set of enum values so that they can be combined with bit-wise or and extracted with bit-wise and. I would prefer that or-ing invalid combination would be detectable.

Are there any tools for generating enums like this?

Edit for clarity

I'm looking for something that can leverage the fact that some flags are invalid in combination to reduce the number of bits used. The requirement that I be able to detect errors is soft. I don't need to be able to tell what was used if things are mucked up.

I'm using C#, but any solution should be helpful.

An example pattern would be:

0011 00
0101 00
1001 00
0110 00
1010 00
1100 00

0000 01
0000 10

that gets 6 exclusive flags and an orthogonal pair of 2 into 6 bits

a quick test show that 5-bits gives 9 values, 6-bits give 20,...


Solution

  • To represent an "exclusive" set of n options (i.e. exactly one must be chosen), we require at least ceil(log2(n)) bits. For example, option k can be represented by the number k in base-2.

    To represent an "orthogonal" set of n options (i.e. any combination of size 0, 1, ..., n can be chosen), we require at least n bits. For example, options k0, k1, k2 could be represented by the binary number whose bits are zero except for bits 0, 1, 2.

    So, to represent multiple option sets simultaneously, we add up the number of bits required for each option set (depending on whether it is "exclusive" or "orthogonal") to get the total number of bits required.

    In short, to pick the enum values,

    where offset r is the number of bits used by the preceding option sets.


    Example of how to automate this construction, in Java:

    /**
     * Construct a set of enum values, for the given sizes and types 
     * (exclusive vs orthogonal) of options sets.
     *
     * @param optionSetSizes
     *     number of elements in each option set
     * @param isOptionSetExclusive
     *     true if corresponding option set is exclusive, false if
     *     orthogonal
     * @returns
     *     array of m elements representing the enum values, where 
     *     m is the sum of option set sizes. The enum values are 
     *     given in the order of the option sets in optionSetSizes 
     *     and isOptionSetExclusive.
     */ 
    int[] constructEnumValues(
            int[] optionSetSizes, 
            boolean[] isOptionSetExclusive)
    {
        assert optionSetSizes.length == isOptionSetExclusive.length;
    
        // determine length of the return value
        int m = 0; 
        for (int i = 0; i < optionSetSizes.length; i++) m += optionSetSizes[i];
        int[] vals = new int[m];
    
        int r = 0; // number of bits used by the preceding options sets
        int c = 0; // counter for enum values used 
    
        for (int i = 0; i < optionSetSizes.length; i++)
        {
            // size of this option set
            int n = optionSetSizes[i];                   
    
            // is this option set exclusive?
            boolean exclusive = isOptionSetExclusive[i]; 
    
            for (int k = 0; k < n; k++)
            {
                vals[c] = (exclusive) ? (k << r) : (0x1 << (r + k));
                c++;
            }
    
            r += (exclusive) ? (int) Math.ceil(Math.log(n)/Math.log(2)) : n; 
        } 
    
        return vals;
    }