2

I use a function like this to convert int (from a legacy API) to an enum:

TestEnum to_test_enum(int value) {
    TestEnum converted(static_cast<TestEnum>(value));
#   pragma GCC diagnostic push
#   pragma GCC diagnostic error "-Wswitch"
    switch (converted) {
        case TestEnum::A:
        case TestEnum::B:
            return converted;
    }
#   pragma GCC diagnostic pop

    throw std::runtime_error("wrong value");
}

to_test_enum throws an exception if an invalid value has been passed to to_enum, otherwise it returns the according TestEnum value. The pragma ensures I will get a compiler error in case a legal value is missing.

Lines like these will now convert an integer and do a validity-check at runtime:

enum class TestEnum {
    A = 1,
    B = 2,
};

auto v1 = to_test_enum(2);
auto v2 = to_test_enum(3);  // will throw

Question: I wonder if it's possible with some sort of template magic to turn this into a function template:

template<class E>
E to_enum(int value) {
    E converted(static_cast<E>(value));
    switch (converted) {
        EACH ELEMENT e IN E: case e:   <--- here goes some unrolling magic
            return converted;
    }

    throw std::runtime_error("wrong value");
}

The function would then be used like this:

auto v1 = to_enum<TestEnum>(2);
auto v2 = to_enum<TestEnum>(3);  // will throw
frans
  • 8,868
  • 11
  • 58
  • 132
  • No, (unless you do some trickery stuff to declare enum with some introspection). – Jarod42 Mar 10 '16 at 16:59
  • If you add a First and Last or other placeholder elements to denote the possible range then that could be possible, but then that forces the enum values to cover a sequential range of values without any holes, not sure if you want to do that. – Rudolfs Bundulis Mar 10 '16 at 17:01
  • I found some preprocessor magic useful which creates several related C++ things from the same text file (e.g. create a map enum -> enumNameString, define enumType_first and enumType_last, and possibly define an inline conversion function). – Peter - Reinstate Monica Mar 10 '16 at 17:03
  • 1
    [This answer](http://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c-and-future-c17/31362042#31362042) (disclaimer: author) describes some of the declaration trickery you could do to achieve this. It doesn't show the `from_int` function, but hopefully it's clear how to implement it, once you can make the declaration. One thing to note is that the obvious conversion you could implement takes time linear in the number of enumerands. – antron Mar 10 '16 at 17:13

1 Answers1

0

I'm not sure that I understood what you're trying to do. See below an idea. You will have to define your own map_enum< your_enum >() in order to map the integers to enums.

#include <map>
#include <iostream>

template< typename E > const std::map< int, E >& map_enum()
{
  return 0;
}

template < typename E > E to_enum( int ai )
{
  auto m = map_enum< E >();
  auto const i = m.find( ai );
  return i == m.end() ? throw -1 : i->second;
}

// { old api
#define RED 0
#define GREEN 1
#define BLUE 2

int old_api_fun()
{
  return 2;
}
// } old api

// { your code
enum color_e
{
  red = RED,
  green = GREEN,
  blue = BLUE
};

template<> const std::map< int, color_e >& map_enum()
{
  static std::map< int, color_e > m ={ { RED, red }, { GREEN, green }, { BLUE, blue } };
  return m;
}
// } your code

int main()
{
  try
  {
    auto r = to_enum< color_e >( old_api_fun() );
    std::cout << "success";
  }
  catch ( int )
  {
    std::cout << "failed";
  }
  return 0;
}
zdf
  • 4,382
  • 3
  • 18
  • 29