Search code examples
c++templatesc++11bitmaskcompile-time-constant

C++ compile-time bitmask addition


I have a number of bitmasks to add (layer, logical OR |), but as they are constants I would like to do so at compile time. Entering advanced template territory...

I tried recursion:

template <uint8_t mask, uint8_t...masks>
struct MaskAdd {
    static const uint8_t value = masks | MaskAdd<masks>::value;
};

template <uint8_t mask>
struct MaskAdd {
    static const uint8_t value = mask;
};

which gave the following errors:

file.cpp:3:55: error: parameter packs not expanded with ‘...’:
  static const uint8_t value = masks | MaskAdd<masks>::value;
                                                       ^
file.cpp:3:55: note:         ‘masks’
file.cpp:7:8: error: redeclared with 1 template parameter
 struct MaskAdd {
        ^
file.cpp:2:8: note: previous declaration ‘template<unsigned char mask, unsigned char ...masks> struct MaskAdd’ used 2 template parameters
 struct MaskAdd {
        ^

I also tried this strange syntax, given by (presumably) a misunderstanding of the cppreference page on parameter packs:

template <uint8_t...masks>
struct MaskAdd {
    static const uint8_t value = (masks | ...);
};

which threw these errors:

file.cpp:3:43: error: expected primary-expression before ‘...’ token
     static const uint8_t value = (masks | ...);
                                           ^
file.cpp:3:43: error: expected ‘)’ before ‘...’ token

I've got a feeling the solution is somewhere in the template<template< region of hell, if anyone can explain those I'd be grateful.


Solution

  • You have typo(s) in this expression:

    masks | MaskAdd<masks>::value
    

    It should be:

    mask | MaskAdd<masks...>::value
    // ^ no 's'          ^ the expansion compiler was talking about
    

    Then it will complain about the redeclaration of the class, so provide a specialization instead (for a single parameter):

    template <uint8_t mask>
    struct MaskAdd<mask> { .. };