I have defined a tuple and its indices by creating an enum class:
/** parameter { key ; value1 ; value1 ; } */
using Parameter = std::tuple<unsigned, unsigned, unsigned>;
enum class ParameterKey : std::size_t {
KEY = 0,
VALUE1 = 1,
VALUE2 = 2
};
Now I would like to get a value from this tuple:
const auto& key = std::get<ParameterKey::KEY>(*parameterPointer);
I thought the implicit conversion from int
to std::size_t
is ensured by the : std::size_t
syntax :
enum class ParameterKey : std::size_t {
....
}
but I'm getting this error
error: no matching function for call to ‘get<KEY>(std::tuple<unsigned int, unsigned int, unsigned int>&)’
This works fine, but it's too garrulous:
const auto& key = std::get<static_cast<unsigned>(ParameterKey::KEY)>(*parameterPointer);
There is no implicit conversion here. From enum:
There are no implicit conversions from the values of a scoped enumerator to integral types, although static_cast may be used to obtain the numeric value of the enumerator.
So, you have to use static_cast
.
There are some workarounds which are based on static_cast
. For instance, one might make use of std::underlying_type
:
template<typename T>
constexpr auto get_idx(T value)
{
return static_cast<std::underlying_type_t<T>>(value);
}
And then:
const auto& key = std::get<get_idx(ParameterKey::KEY)>(*parameterPointer);
The whole purpose of enum class
is to not be implicitly convertible to int
, so there is no implicit conversion.
You could create your own get
version:
template <ParameterKey key, typename Tuple>
decltype(auto) get(Tuple &&tuple) {
return std::get<static_cast<std::underlying_type_t<ParameterKey>>(key)>(tuple);
}
Then:
const auto& key = get<ParameterKey::KEY>(*parameterPointer);
You can make the conversion implicit by creating specialization of an array/vector that accepts this specific enum as an argument:
template <typename ElementType, typename EnumType>
class enumerated_array: array<ElementType, static_cast<size_t>(EnumType::size_)>
{
using ParentType = array<ElementType, static_cast<size_t>(EnumType::size_)>;
public:
ElementType& operator[](EnumType enumerator)
{
return ParentType::operator[](static_cast<size_t>(enumerator));
}
const ElementType& operator[](EnumType enumerator) const
{
return ParentType::operator[](static_cast<size_t>(enumerator));
}
};
// --------------------------------
// and that's how you use it:
enum class PixelColor: size_t { Red, Green, Blue, size_ };
enumerated_array<uint8_t, PixelColor> pixel;
// Can't use any other enum class as an index
pixel[PixelColor::Green] = 255;
Also, while this is not a topic of this question, this approach synergies really well with enum iterators:
template <typename T>
class EnumRangeType
{
public:
class Iterator
{
public:
Iterator(size_t value):
value_(value)
{ }
T operator*() const
{
return static_cast<T>(value_);
}
void operator++()
{
++value_;
}
bool operator!=(Iterator another) const
{
return value_ != another.value_;
}
private:
size_t value_;
};
static Iterator begin()
{
return Iterator(0);
}
static Iterator end()
{
return Iterator(static_cast<size_t>(T::size_));
}
};
template <typename T> constexpr EnumRangeType<T> enum_range;
// --------------------------------
// and that's how you use it:
void make_monochrome(enumerated_array<uint8_t, PixelColor>& pixel)
{
unsigned int total_brightness = 0;
for (auto color: enum_range<PixelColor>)
total_brightness += pixel[color];
uint8_t average_brightness = total_brightness/3;
for (auto color: enum_range<PixelColor>)
pixel[color] = average_brightness;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With