2

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.

max66
  • 65,235
  • 10
  • 71
  • 111
Kisss256
  • 25
  • 7
  • 1
    The latter (masks | ...) is currently not even C++17 (AFAIK), there are high hopes to get it .. sometime. Also, it's not clear why you don't just OR the values. When both sides of `|` are compile-time constants, the compiler can - and will - optimize it out. – lorro Jun 24 '16 at 10:43
  • Did you try `constexpr`? Also I suspect if you just make them `const` the compiler will do the math at compile time anyway. – Galik Jun 24 '16 at 10:50
  • Good points @lorro and Galik, I guess I don't have that much faith in the compiler. – Kisss256 Jun 24 '16 at 12:21

2 Answers2

4

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> { .. };
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • Verified. Sorry for that silly typo. That specialization syntax is something I have to look into. – Kisss256 Jun 24 '16 at 12:31
  • How could I extend this solution to handle different types, but still use the same `MaskAdd::value` syntax? – Kisss256 Jun 27 '16 at 01:09
  • @Kisss256 I've done it like [that](http://coliru.stacked-crooked.com/a/2cc09e46cb6a914c). You could do it with a `class`, but the type would need to be the first parameter. It will be better if you look into `constexpr`. – LogicStuff Jun 27 '16 at 08:22
0

This is my aproach for a compile time mask... First templated parameter is the position of the bit counting from right, the second is the number of bits set as 1 towards left.

template <unsigned START, unsigned RANGE>
struct Mask
{
    static const size_t val = 1 << START | Mask<START + 1, RANGE - 1>::val;
};

template <unsigned START>
struct Mask<START, 0>
{
    static const size_t val = 0;
};

If I want to create a mask with, for example, number 14 (0000 1110):

unsigned mask = Mask<1, 3>::val;
bitxuro
  • 1
  • 1