Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if enum class contains a specific identfier

I searched a bit here on SO and was surprise that I didn't find any similar question. Happy for any hints in case this has already been answered.

I have a codebase with a lot of enum classes defined. Some of them specify a totalNum constant like

enum class Foo : int
{
    a,
    b,
    c,

    totalNum
}

Others don't have this like

enum class Bar : bool
{
    oneOption,
    otherOption
}

Then I have a function basically like this

template <class EnumClassType>
EnumClassType typeToEnum (typename std::underlying_type<EnumClassType>::type value)
{
    // If you hit this assertion, the value is outside of the valid enum range
    assert (isPositiveAndBelow (value, decltype (value) (EnumClassType::totalNum)));

    return EnumClassType (value);
}

While this works and makes sense for the an enum with totalNum specified, I'd like to skip this assert in case there is no such identifier in the enum. Is there any way to do that? The codebase is currently using C++ 14 but C++ 17 solutions are also welcome due to an upcoming compiler change.

like image 919
PluginPenguin Avatar asked Jul 21 '21 15:07

PluginPenguin


1 Answers

Found the answer myself in the meantime, using an approach like @jfh mentioned in the comments.

First of all this is a way to check if an enum class contains an identifier with a certain name

template <class EnumToTest>
class EnumConstantDefined_totalNum
{
private:
    using Yes = int8_t;
    using No = int16_t;

    template <class E>
    static Yes test (decltype (E::totalNum)*);

    template <class E>
    static No test (...);

public:
    static constexpr bool value = sizeof (test<EnumToTest> (0)) == sizeof (Yes);
};

Then we can employ SFINAE to specify two overloads for both kinds of enums.

template <class EnumType>
std::enable_if_t<EnumConstantDefined_totalNum<EnumType>::value, void> assertValueIsInRange (typename std::underlying_type<EnumType>::type value)
{
    assert (isPositiveAndBelow (value, decltype (value) (EnumType::totalNum)));
}

template <class EnumType>
std::enable_if_t<! EnumConstantDefined_totalNum<EnumType>::value, void> assertValueIsInRange (typename std::underlying_type<EnumType>::type)
{
    // do nothing
}

Then use this assertion function in the actual conversion function

/**
   Casts a value matching an enum class underlying type to an enum class constant and asserts that the
   value is inside the valid enum range
 */
template <class EnumClassType>
EnumClassType typeToEnum (typename std::underlying_type<EnumClassType>::type value)
{
    assertValueIsInRange<EnumClassType> (value);

    return EnumClassType (value);
}
like image 193
PluginPenguin Avatar answered Nov 02 '22 00:11

PluginPenguin