C ++: ensure that enum values ​​are unique at compile time

I have the following enumwhich describes the error codes:

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

The main reason I explicitly write enum values ​​is to make debugging easier.
In any case, I wonder if there is a way to make the compiler complain about non-unique values . I can always check it at runtime, but I would like to avoid it.

I read this post and reviewed this answer . As far as I understand, this answer involves generating an enumeration in such a way that "making it much harder to make mistakes."
I would like to leave the definition of the listing as is or close to it.

+4
source share
4 answers

, Boost , , enum . mpl::vector, , vector . is_unique , .

#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;
}

:

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

static_assert failed "enum values not unique".

+4

, , . , , :

awk 'NR>1{print $1, i; i +=1} eerror.h

, eerror.h :

typedef enum {
+1

++ .

, enum (. ), - gccxml, doxygen - .

, , , __LINE__ , , . , .

( - , , .., <[{( ....

#include <iostream>
#include <string>
#include <map>

namespace Benum
{
    struct Meta
    {
        Meta(const char* p, int* p_values)
        {
            while (*p)
            {
                if (isalnum(*p) || *p == '_')
                {
                    const char* p_from = p;
                    while (isalnum(*p) || *p == '_')
                        ++p;
                    std::string idn = std::string(p_from, p - p_from);
                    int_to_string_[*p_values] = idn;
                    string_to_int_[idn] = *p_values;
                    ++p_values;
                }
                else if (*p == '=')
                    while (*p && *p != ',')
                        ++p;
                else
                    ++p;
            }
        }
        std::ostream& out(std::ostream& os, int i) const
        {
            Int_To_String::const_iterator it = int_to_string_.find(i);
            if (it != int_to_string_.end())
                return os << it->second;
            else
                return os << "<unmatched enum " << i << '>';
        }
        typedef std::map<int, std::string> Int_To_String;
        std::map<int, std::string> int_to_string_;
        std::map<std::string, int> string_to_int_;
    };

    template <typename T>
    struct Incrementing
    {
        Incrementing(int n) : n_(n) { s_next_implicit_ = n + 1; }
        Incrementing() : n_(s_next_implicit_++) { }
        operator int() const { return n_; }
        int n_;
        static int s_next_implicit_;
    };

    template <typename T>
    int Incrementing<T>::s_next_implicit_;
}

#define BENUM(IDN, ...) \
    enum IDN ## _Enum { __VA_ARGS__ }; \
    struct IDN { \
        typedef IDN ## _Enum Enum; \
        IDN(Enum e) : e_(e) { } \
        IDN& operator=(Enum e) { e_ = e; return *this; } \
        operator Enum() const { return e_; } \
        friend std::ostream& operator<<(std::ostream& os, Enum e) { \
            return IDN::meta().out(os, e); \
        } \
        static const Benum::Meta& meta() { \
            static Benum::Incrementing<IDN> __VA_ARGS__; \
            static int values[] = { __VA_ARGS__ }; \
            static Benum::Meta m(#__VA_ARGS__, values); \
            return m; \
        } \
        Enum e_; \
    };

// benum example usage...

BENUM(My_Enum, One = 1, Two = 2, Three = 3, Four = 4, Whats_Next);

int main()
{
    std::cout << One << ' ' << Two << ' ' << Three << ' ' <<
        Whats_Next << '\n';
    const Benum::Meta& meta = My_Enum::meta();
    for (std::map<int, std::string>::const_iterator i = meta.int_to_string_.begin();
        i != meta.int_to_string_.end(); ++i)
        std::cout << i->first << ' ' << i->second << '\n';
}
+1
source

This solution modifies the definition of the enum, so if you can come to terms with it ...

It also suggests that the values ​​are mostly consistent.

Do not specify values. After defining the enumeration, static_assert checks that people know the values.

enum EErrorType : uint16_t
{
  et_general = 0,
  et_INVALID_CLI_FLAG,
  et_FOO,
  et_BAR,
  ...
  et_undef = 500
};
static_assert(EErrorType::et_general          == 0, "Wrong enum value");
static_assert(EErrorType::et_INVALID_CLI_FLAG == 1, "Wrong enum value");
static_assert(EErrorType::et_FOO              == 2, "Wrong enum value");
static_assert(EErrorType::et_BAR              == 3, "Wrong enum value");
...
static_assert(EErrorType::et_undef            == 500, "Wrong enum value");

That way, they are basically automatically assigned and therefore unique, and you can also have human-readable values ​​for debugging and other purposes.

+1
source

Source: https://habr.com/ru/post/1523877/


All Articles