Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting a valid random enum value in a general way

Let's say we have an enumerated type E.

enum class E : underlying_type_of_E {
 v1 = uE1,
 v2 = uE2,
   //...
 vN = uEN
};

typedef typename std::underlying_type<E>::type uE;

In general, not all values of uE are valid values of E, because we can choose the relationship between them. Is there a general way of creating random, valid (named in definition, not assignable), values of E? This, for example would not work:

std::mt19937 m;
std::uniform_int_distribution<uE> randomUE(0, std::numeric_limits<uE>::max());

E e = static_cast<E>( randomUE(m) );

because:

  1. Value range may not start from 0
  2. Value range may not end at std::numeric_limits::max()
  3. Value range may not be a range at all - we can select discrete values for E from uE, for example {1, 3, 64, 272}.

Given that all of the enumerated values are known at compile-time, I cannot imagine a reason why would this be in any way dangerous, or error-prone.

As for a context of why I want such a thing - I'm working on a genetic algorithm that uses templated gene storage. For now, I use enums as chromosomes and store them in std::vector<bool> which is converted into std::vector<enumT> on demand. The problem with this approach is mutation that flips random bits with given probability. That can cause problems, as it can produce invalid chromosomes that are unnamed enum values.

like image 618
tsuki Avatar asked Oct 31 '22 18:10

tsuki


1 Answers

You can do this if you're prepared to use a preprocessor macro to create both the enum type and some meta-data about it, but it's a minor hassle:

  • Invoke a variadic macro:

    ENUM(E,
         v1 = uE1,
         v2 = uE2,
         // ...
         vN = uEN);
    
  • Create a templated class Incrementing where successive variables are initialised by an incrementing static member by default, but can be assigned to from whatever your underlying type is (e.g. int).

    static Incrementing<E, Underlying> __VA_ARGS__; \
    
  • Use the values above to initialise a array with the Incrementing values (which need an operator Underlying() const member).

    static const Underlying values[] = { __VA_ARGS__ }; \
    

The values[] array then contains the named enumeration values....

There's a total overkill version of this concept I wrote years ago here, but I'd recommend just starting from scratch given your simple requirements.

like image 66
Tony Delroy Avatar answered Nov 09 '22 16:11

Tony Delroy