ereOn
ereOn

Reputation: 55776

Is it possible to generate an automatic `switch()` directive from several template specializations?

I am implementing a protocol in which one host can receives one-byte commands.

While a byte has 256 possible values, only a few of them are valid.

Of course I can write something like:

bool is_valid(uint8_t command)
{
  switch (command)
  {
    case 0x00:
    case 0x01:
    case 0xa0:
    case 0xa1:
      return true;
  }

  return false;
}

To check at runtime the validity of the received commands.

Unfortunately, I also have to maintain a list of names for each and every of these commands, which would then force me to write another function very close to is_valid() but which would return the name of the command instead. Doing so would make me duplicate the list of valid commands, and I'm not fond of that.

I wondered if there was maybe a way of using meta-programming to declare the valid commands only once.

Something like:

template <uint8_t Value>
struct valid_value_type;

struct valid_value_type<0x00> { static const std::string name = "Stop command"; };
struct valid_value_type<0x01> { static const std::string name = "Start command"; };

However, I couldn't reach anything satisfying (understand "compiling & working") and I don't know how to generate an adaptative is_valid() function that would automatically consider the existence of these template declarations.

Is that even possible ? If so, how would you do it ?

Note: Sadly C++11 is not an option, but I'm interested in the solution as well out of curiosity.

Upvotes: 3

Views: 143

Answers (2)

Tomek
Tomek

Reputation: 4659

Would something along the below lines do?

template <uint8_t Value>
struct valid_value_type
{
  static char const *const name = 0;
};

template<>
struct valid_value_type<0x00>
{
  static char const *const name = "Stop command";
};

template<>
struct valid_value_type<0x01>
{
  static char const *const name = "Start command";
};

template <uint8_t N>
bool is_valid_value_type_helper(uint8_t value, valid_value_type<N>)
{
  return((value == N) ? valid_value_type<N>::name != 0 : check_valid_value_type_helper(value, valid_value_type<N-1>()));
}

bool is_valid_value_type_helper(uint8_t value, valid_value_type<0>)
{
  return(value == 0 && valid_value_type<0>name != 0);
}

bool is_is_valid_value_type(uint8_t value)
{
  return(is_valid_value_type_helper(value, valid_value_type<0xff>());
}

It probably can be simplified a bit...

Upvotes: 1

Unless you are in an embedded environment, there are only 256 possible values for a uint8_t, so it would not be horrible to write a lookup table with that...

char const * cmds[256] = {};
void cmds_init(char const * (&cmds)[256]) {
   cmds[0x00] = "Stop command";
// ...
}

Then the test for a valid command is just finding the name in the lookup table:

bool is_valid(uint8_t cmd) {
   return cmds[cmd] != 0;
}

If the number of valid commands is much smaller than 256, you can implement a function that uses a switch to return the names, or null if the command is not know, and use the same approach.

Upvotes: 2

Related Questions