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.
--- EDIT 2 ---: As pointed by ildjarn (thanks!) this solution is very dangerous if in the Args...
types are some non POD (Plain Old Data) type.
It's explained well in this page.
I transcribe the relevant part
An example of a type that cannot be safely copied using memcpy is
std::string. This is typically implemented using a reference-counted
shared pointer, in which case it will have a copy constructor that
causes the counter to be incremented. If a copy were made using memcpy
then the copy constructor would not be called and the counter would be
left with a value one lower than it should be. This would be likely to
result in premature deallocation of the memory block that contains the
character data.
--- EDIT 3 ---
As pointed by ildjarn (thanks again!) with this solution is very dangerous to leave the data()
member.
If anyone use the pointer returned in this way
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f { *(float*)(pv+pos) }; // <-- risk of unaligned access
could, in some architecture, cause an access to a float *
in an unaligned address that can kill the program
The correct (and safe) way to recover values from the pointer returned by data()
is the one used in toCoutHelper()
, using `std::memcpy()
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f;
std::memcpy( & f, pv + pos, sizeof(f) );