mercredi 27 juin 2018

How Can I Build A Sequence Of Structs With Integer Members of Increasing Value at Compile Time in C++11

Working in an embedded environment, I'm repeatedly writing code that takes an array of bytes from a protocol layer and turns those bytes into a C++ class representation.

An example array of bytes that represents a uint32_t, followed by a uint8_t, followed by a uint16_t might look like this.

std::array<uint8_t, 7> bytes(0x01, 0x02, 0x03, 0x04, 0x10, 0x20, 0x30);

Where 0x01020304 is my uin32_t, 0x04 is my uint8_t and 0x2030 is my uint16_t.

I also have a variadic function func that I want to call with the values parsed out of the payload.

To achieve this, I manually define an intermediate object:

// Declaring the Object
struct MY_TYPE
{
   uint32_t val1;
   uint8_t val2;
   uint16_t val3;
} __attribute__((__packed__));

// Processing the Bytes 
auto & object(reinterpret_cast<MY_TYPE *>(&bytes));

func(object.val1, object.val2, object.val3) 

What I want to do is implement a variadic class such that I don't need to re-implement MY_TYPE for every combination of types.

Here's what I initially tried:

template <typename... Types>
struct GENERIC_CLASS
{
   template <typename ReturnType, std::size_t ArraySize>
   ReturnType getValueFromArray(std::array<uint8_t, ArraySize> const & array, 
                                uint32_t & index); 

   // Note, not valid c++ since the size of the array (N) isn't 
   // specified. This has been omitted for simplicity. 
   void process(std::array<uin8_t, N> const & array)
   {
      auto currentIndex(u0);

      // Assumes this class has a specialization 
      // for getValueFromArray for all of the types in Types. 

      // This code doesn't work because there is no sequence point 
      // between each call to getValueFromArray, so the 
      // currentIndex can be incremented in a non-deterministic way. 
      func(this->getValueFromArray<Types>(array, currentIndex)...);
   }
};

I was able to work around this problem by introducing a new class:

template <typename T, std::size_t position>
struct Param
{
   using type = T;
   static constexpr std::size_t offset = position;
};

This way, instead of maintaining currentIndex at runtime, I can specify the offset of each argument in code, like this:

GENERIC_CLASS<Param<uint32_t, 0>, Param<uint8_t, 4>, Param<uint16_t, 5>>

The above is potentially error prone, as the offsets could be wrong. Is there some way to generate my sequence of Params from a parameter pack of types by accumulating sizes?

Alternatively, is there some workaround for the sequence point problem that I've mentioned above?

Aucun commentaire:

Enregistrer un commentaire