Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++: ensure enum values are unique at compile time

Tags:

c++

enums

unique

I have the following enum that describes error codes:

 typedef enum {
    et_general           = 0,
    et_INVALID_CLI_FLAG  = 1,
    ...
    et_undef = 500
  } EErrorType;

The main reason why I explicitly write the enum values, is to ease the debug process.
Anyways, I wonder if there's a way, to make the compiler complain about non unique values. I can always check it at run time easily, but I'd like to avoid that.

I've read this post and reviewed this answer. As I understand, that answer suggests to generate the enum in such way that it "make it much harder to make mistakes".
I'd would like to leave the enum definition as is, or close to it.

like image 547
idanshmu Avatar asked Jan 27 '14 10:01

idanshmu


2 Answers

I'm not sure if Boost is available in your scenario, so here is a solution where the enum has to be defined in a pre-processor sequence. That sequence is then used to build the enum and a corresponding mpl::vector and we compute if the elements of the vector are unique in an odd-fashion. We might want to define a proper is_unique algorithm first, but this should do.

#include <boost/mpl/vector.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/unique.hpp>
#include <boost/mpl/size.hpp>

#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>


#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))

#define GET_NAME(_, __, elem) BOOST_PP_TUPLE_ELEM(2, 0, elem) = BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define GET_VALUE(_, __, elem) boost::mpl::int_<BOOST_PP_TUPLE_ELEM(2, 1, elem)>

enum E {
  BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_NAME, _, MYENUM))
};

typedef boost::mpl::sort< 
  boost::mpl::vector<
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_VALUE, _, MYENUM))
    > 
  >::type evalues;

typedef boost::mpl::unique< evalues, boost::is_same<boost::mpl::_1, boost::mpl::_2> >::type uniqued;
static_assert(boost::mpl::size<uniqued>::value == boost::mpl::size<evalues>::value, "enum values not unique");

int main()
{

  return 0;
}

If you change the enum definition to:

#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))((BAZZ, 2))

you will get an error stating the static_assert failed "enum values not unique".

like image 125
pmr Avatar answered Oct 18 '22 09:10

pmr


One can write a dummy switch statement with the enum values as labels – that will guarantee their uniqueness. Placed in a dummy unreferenced function, it won't get into the executable.

like image 44
eugensk Avatar answered Oct 18 '22 08:10

eugensk