dempzorz dempzorz - 2 months ago 6
C++ Question

Store arbitrary elements in contiguous memory

I am trying to create a data structure, where it will hold N number of different types in contiguous memory. So at compile time I can say I want to store 4 elements of 3 different types, and in memory it will look like 111122223333.

I've been going with a variadic template approach, which I think will do what I want, however I am not sure how to add the elements to each array in the add method.

template<std::size_t N, typename... Args>
class Batch
{
private:
std::tuple<std::array<Args, N>...> data_;
size_t currentPos_;

public:
template<typename T>
void addToArray(std::array<T, N>& array, const T& value)
{
array[currentPos_] = value;
}

void add(const Args&... values)
{
//????
addToArray(/*array, value*/);

currentPos_++;
}

const void* data()
{
&return data_;
}
};


int main()
{
Batched<3, float, double, int> b;

b.add(1.0f, 1.0, 1);
b.add(2.0f, 2.0, 2);
b.add(3.0f, 3.0, 3);
b.add(4.0f, 4.0, 4);
return 0;
}


Even if I get this to work, will the memory layout be correct? Is there a better approach?

Answer

I don't think it's a good idea but... I show it just for fun

Using a std::vector<char> (and the access to the following memory granted by the C++11 added method data()) and the good-old memcpy(), I suppose You can simply do as follow

#include <vector>
#include <cstring>
#include <iostream>

template <typename... Args>
class Batch
 {
   private:
      std::vector<char> buffer;

   public:

      void addHelper ()
       { }

      template <typename T, typename ... Ts>
      void addHelper (T const & v0, Ts ... vs)
       { 
         auto  pos = buffer.size();

         buffer.resize(pos + sizeof(T));

         std::memcpy(buffer.data() + pos, & v0, sizeof(T));

         addHelper(vs...);
       }

      void add (const Args&... values)
       { addHelper(values...); }

      const void * data()
       { return buffer.data(); }

      void toCout ()
       { toCoutHelper<Args...>(0U, buffer.size()); }

      template <typename T, typename ... Ts>
      typename std::enable_if<(0U < sizeof...(Ts)), void>::type
         toCoutHelper (std::size_t  pos, std::size_t  size)
       {
         if ( pos < size )
          {
            T val;

            std::memcpy( & val, buffer.data() + pos, sizeof(T) );

            std::cout << " - " << val << std::endl;

            toCoutHelper<Ts...>(pos+sizeof(T), size);
          }
       }

      template <typename T, typename ... Ts>
      typename std::enable_if<0U == sizeof...(Ts), void>::type
         toCoutHelper (std::size_t  pos, std::size_t  size)
       {
         if ( pos < size )
          {
            T val;

            std::memcpy( & val, buffer.data() + pos, sizeof(T) );

            std::cout << " - " << val << std::endl;

            toCoutHelper<Args...>(pos+sizeof(T), size);
          }
       }

 };


int main()
 {
   Batch<float, double, int> b;

   b.add(1.0f, 1.0, 1);
   b.add(2.0f, 2.0, 2);
   b.add(3.0f, 3.0, 3);
   b.add(4.0f, 4.0, 4);

   b.toCout();

   return 0;
 }

--- EDIT ---: added a method, toCout() that print (to std::cout) all the stored values; just to suggest how to use the values.

Comments