0

I have an enumerator which can be used as a uint8_t and I want to use a combination of two values of this enumerator to create a number of type uint16_t where the leftmost and the rightmost bytes are determined by these 2 enumeration values.

I then need to use this resulting value as the key element type of a unordered_map<uint16_t, something> where I store several something values.

So I came up with this code which resolves two enumeration values into a key at run-time. The comments just indicate that the real problem involves more than the elements showed in the example below.

enum Value : uint8_t {
    V1, V2, V3, V4 // , ... , VN
};

static inline uint16_t compose(Value a, Value b) {
    return ((uint16_t) a << 8) | b;
}

static const unordered_map<uint16_t, something> elements = {
    { compose(Value::V3, Value::V1), something1 },
    { compose(Value::V4, Value::V4), something2 },
    { compose(Value::V1, Value::V2), something3 },
    // { ... }, { compose(Value::VA, Value::VB), somethingN }
};

If I'm not mistaken, compose will be called several times when the program starts and tries to initialise the variables and the constants before running main.

Now the big question:

Is there a better way to do this in order to compose the number at compile-time instead of runtime?

I could potentially hardcode the numbers instead of running compose, but that would be a very ugly and possibly unsafe solution.

Imagine if I had to switch the position of two or more "values" of the enumerator; that would require a complete revision of the hardcoded numbers so that's not an option at all.

It also would be very nice to maintain the names of the two enumeration values that are composing a uint16_t so when I need to edit the something corresponding to V1 and V4 I can find it easily.

Unfortunately I don't think that the preprocessor can do something like this and I doubt that the compiler is able to optimise compose even though I used the inline keyword.

Please help me.

Cristian
  • 654
  • 1
  • 13
  • 30
  • This may be a good use for a macro, since macro's are processed before the interpretation of the code. With a macro, there would be no function calls. – Thomas Matthews May 13 '20 at 16:14
  • Here's an example of compile-time mapping https://stackoverflow.com/a/16491172/3365922 – fas May 13 '20 at 16:16
  • *"It also would be very nice to maintain the names of the two enumeration values that are composing a uint16_t so when I need to edit the something corresponding to V1 and V4 I can find it easily."* - this is ambiguous. If you want to find their names in the source code, you can already do that with your `compose` function approach, though it might be easier if you put them in e.g. alphanumeric order for easier regexp matching. If you want to index the container and update contents, using say a string with the two enumeration identifiers, then you need supporting identifier->enum function. – Tony Delroy May 16 '20 at 03:01
  • @TonyDelroy You're absolutely right. That's why I chose the `consteval` approach in answers below. It maintains the compose function and it will be resolved at compile-time and turned into a number. So in the end I can always use regex search to find what I need in the code. – Cristian May 17 '20 at 12:00

2 Answers2

3

In c++20, you can make compose a consteval function. Then it's guaranteed to be evaluated at compile time:

consteval std::uint16_t compose(Value a, Value b) {
    return ((std::uint16_t) a << 8) | b;
}

Here's a demo.

Note that you won't be able to call this function at run-time if you need to.

cigien
  • 57,834
  • 11
  • 73
  • 112
1

You could mark compose as a constexpr function. That way, the compiler might be able to use the result of the function as a compile-time constant:

inline constexpr uint16_t compose(Value a, Value b) { ... }
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    But, as not used in constant expression, it is mostly just an hint for compiler (which might already does this optimization with constant propagation and inlining). – Jarod42 May 13 '20 at 16:54