requirements
- Map non-integer values to scoped enum literals
- Use switch statements to catch missing enum cases in value lookup (
-Wswitch-enum
allows default case) - Use class template to iterate the enum mapped values
Limitations
- Iterator needs to be given begin/end points, so you have to sync this end limit with the last entry in EncapsulatedEnum::Key
full example
#include <iostream>
#include <cstdint>
#include <array>
#include <iomanip>
#include <type_traits>
// The unsigned 16bit value type used by EncapsulatedEnum class,
// includes serialization ctor and deserialize function (to_uint16)
class U16
{
public:
U16() = default;
U16(uint8_t b1, uint8_t b2) { m_data[0] = b1; m_data[1] = b2; }
U16(uint16_t s2) { m_data[0] = s2 >> 8; m_data[1] = s2; }
uint16_t to_uint16() const { return (m_data[0] << 8 | m_data[1]); }
bool operator==(U16 const& rhs) { return (this->to_uint16() == rhs.to_uint16()) ? true : false; }
private:
std::array<uint8_t, 2> m_data{0x00, 0x00};
};
// iterator for use with enums
template < typename ENUM, ENUM beginVal, ENUM endVal>
class EnumIterator {
typedef typename std::underlying_type<ENUM>::type val_t;
int val;
public:
EnumIterator(const ENUM &f) : val(static_cast<val_t>(f)) {}
EnumIterator() : val(static_cast<val_t>(beginVal)) {}
EnumIterator operator++() {
++val;
return *this;
}
ENUM operator*() { return static_cast<ENUM>(val); }
EnumIterator begin() { return *this; } //default ctor is good
EnumIterator end() {
static const EnumIterator endIter = ++EnumIterator(endVal); // cache it
return endIter;
}
bool operator!=(const EnumIterator& i) { return val != i.val; }
};
// Singleton class provides forward/reverse lookup
// on member enum key/value
class EncapsulatedEnum
{
public:
// The list of keys
enum class Key
{
ONE,
THREE,
FIVE,
SIX,
TEN
};
// Iterator needs to be given begin/end points, so you have to
// sync this end limit with the last entry in EncapsulatedEnum::Key
typedef EnumIterator<Key, static_cast<Key>(0), Key::TEN> KeyIter;
// Return a U16 value depending on the key given
static U16 getval(Key k)
{
// guarantee singleton creation
[[maybe_unused]] static EncapsulatedEnum instance;
switch(k)
{
case Key::ONE:
return U16{0x00, 0x01};
case Key::THREE:
return U16{0x00, 0x03};
case Key::FIVE:
return U16{0x00, 0x05};
case Key::SIX:
return U16{0x00, 0x06};
case Key::TEN:
return U16{0x00, 0x10};
// case Key::FIFTY:
// return U16{0x00, 0x50};
// default: do not uncomment, catch missing enum cases at compile time.
}
// this line can never be called but keeps compiler warning quiet
__builtin_unreachable();
}
// Return a key depending on the U16 value given
static KeyIter getkey(U16 val)
{
// iterate the enum and check values against the arg
for (auto k: KeyIter()) { if (getval(k) == val) { return k; } }
return KeyIter().end();
}
private:
EncapsulatedEnum() {}
};
// test helper function
void validate(EncapsulatedEnum::KeyIter foundkey)
{
// deref the iterator back to enum type, it is impossible
// to omit a case providing 'default' is not used.
switch(*foundkey)
{
case EncapsulatedEnum::Key::ONE:
std::cout << "Found Key::ONE\n";
return;
case EncapsulatedEnum::Key::THREE:
std::cout << "Found Key::THREE\n";
return;
case EncapsulatedEnum::Key::FIVE:
std::cout << "Found Key::FIVE\n";
return;
case EncapsulatedEnum::Key::SIX:
std::cout << "Found Key::SIX\n";
return;
case EncapsulatedEnum::Key::TEN:
std::cout << "Found Key::TEN\n";
return;
// case EncapsulatedEnum::Key::FIFTY:
// std::cout << "Foudn Key::FIFTY\n";
// return;
// default: do not uncomment, catch missing enum cases at compile time.
};
// foundkey == 'end()'
std::cout << "Key not found\n";
}
int main()
{
// Get the value mapped to a specific Enum type (key)
std::cout << EncapsulatedEnum::getval(EncapsulatedEnum::Key::ONE).to_uint16() << std::endl;
//std::cout << EncapsulatedEnum::getval(EncapsulatedEnum::Key::FOUR).to_uint16() << std::endl; // compiler error
std::cout << EncapsulatedEnum::getval(EncapsulatedEnum::Key::FIVE).to_uint16() << std::endl;
// Get the Enum type (key) mapped to specific value
validate(EncapsulatedEnum::getkey(U16{0x00, 0x01}));
validate(EncapsulatedEnum::getkey(3));
validate(EncapsulatedEnum::getkey(U16{0x00, 0x50}));
std::cout << std::endl << sizeof(EncapsulatedEnum::Key) << std::endl;
return 0;
}
output
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1
5
Found Key::ONE
Found Key::THREE
Key not found
compiler explorer example
single file:
https://godbolt.org/z/eGf1KP7Ee