0

I thought this should be easy, but I've been struggling with it for a while so I thought I should ask here.

I want to make a template metafunction that takes a type corresponding to a C++11 enum class as its argument, and returns an int:

  • If the enum class E has an enumerator value a, then return static_cast<int>(E::a).
  • If the enum class E does not have an enumerator value a, then return 42.

Then I would like to make a template function that takes a run-time instance of some enum class E, static casts it to int, and checks if it matches this metafunction.

I tried many iterations of, templating a struct and use template partial specialization to try to distinguish if E::a exists, also using function templates... I'm not sure if I can reconstruct everything that I tried but here's the most recent iteration:

template <typename E>
inline int get_a_int_val(int result = E::a) { return result; }

template <typename E>
inline int get_a_int_val(int result = 42) { return result; }

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>();
}

This doesn't work, because I'm redefining default arguments.

template <typename E, int result = E::a>
inline int get_a_int_val() { return result; }

template <typename E, int result = 42>
inline int get_a_int_val() { return result; }

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>();
}

This doesn't work, because non-type parameters cannot depend on type parameters.

template <typename E>
struct get_a_int_val {
  static const int value = 42;
};

template <typename E>
struct get_a_int_val<E> {
  static const int value = static_cast<int>(E::a);
};

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>::value;
}

This doesn't work, because

error: 
  class template partial specialization does not specialize any template
  argument; to define the primary template, remove the template argument
  list

What's the correct way to do this?


Motivation:

The reason that I want to do this is, I want to work around what seems to be a bug in libstdc++ which I reported here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68307

In C++11 in the <system_error> header, there are a bunch of enumerator values for std::errc which are supposed to be defined, however, on mingw some of them are missing. This causes compilation errors in my program because, depending on how Asio is configured, lib::asio::errc may be typedef'd to std::errc, and websocketpp assumes that lib::asio::errc::operation_canceled is a defined symbol. I wanted to throw together some kind of shim that I can put around that in the websocketpp code so that it will be defined acceptably on any platform (either to lib::asio::errc::operation_canceled if it exists, or to ECANCELED from <cerrno> if not.)

Chris Beck
  • 15,614
  • 4
  • 51
  • 87

2 Answers2

2

You can do this in many ways, one of them is the following:

template <typename E, typename Enable = void>
struct get_a_int_val {
  static const int value = 42;
};

template <typename E>
struct get_a_int_val<E, typename std::enable_if<std::is_same<decltype(E::a), 
                                                decltype(E::a)>::value, void>::type>{
  static const int value = static_cast<int>(E::a);
};

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168
1

You may create a traits for that:

template <typename E>
std::false_type has_a_impl(...);

template <typename E> auto has_a_impl(int) -> decltype(E::a, std::true_type{});

template <typename E>
using has_a = decltype(has_a_impl<E>(0));

And then use it in SFINAE:

template <typename E>
std::enable_if_t<has_a<E>::value, int>
get_a_int_val() { return static_cast<int>(E::a); }

template <typename E>
std::enable_if_t<!has_a<E>::value, int>
get_a_int_val() { return 42; }

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302