Я не думаю, что это хорошая идея, но ... я показываю это просто для удовольствия
Использование std::vector<char>
(и доступ к следующему памяти, предоставленного C++ 11 добавленным методом data()
) и старый-добрый memcpy()
, я полагаю, вы можете просто сделать, как следовать
#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 ---: добавлен метод, toCout()
, который печатает (до std::cout
) все сохраненные значения; просто чтобы предложить, как использовать значения.
--- EDIT 2 ---: (спасибо!) Как указано на Илдъярн это решение очень опасно, если в Args...
типов некоторые не POD (Plain Old Data) типа.
Это хорошо объяснено в this page.
Я переписываю соответствующей части
В качестве примера такого типа, который не может быть безопасно скопирован с помощью тетсра является зОй :: строки. Обычно это реализуется с использованием общего общего указателя , и в этом случае он будет иметь конструктор копирования, который заставляет счетчик увеличиваться. Если копия была сделана с использованием memcpy , тогда конструктор копирования не будет вызываться, а счетчик будет слева со значением, которое будет ниже, чем должно быть. Вероятно, это приведет к , что приведет к преждевременному освобождению блока памяти, содержащего знаковые данные .
--- EDIT 3 ---
Как указано на Илдъярн (спасибо еще раз!) С этим решением, очень опасно оставлять data()
члена.
Если кто-нибудь использовать указатель, возвращенный таким образом
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f { *(float*)(pv+pos) }; // <-- risk of unaligned access
может, в какой-то архитектуры, вызывают доступ к float *
в качестве выровненным адрес, который может убить программу
Правильное (и безопасное) способ восстановить значения из указателя, возвращаемого data()
является один используется в toCoutHelper()
, используя `зЬй :: тетсра()
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f;
std::memcpy(& f, pv + pos, sizeof(f));
модифицированного мои ответьте на возможную проблему, указанную Ildjarn; вкратце: ** никогда не использовать мое решение, если в 'Args ...' не являются типы POD – max66
Немного контекста, почему я задал вопрос и почему я принял ответ, даже с предупреждениями. В конечном итоге это будет использоваться для передачи буфера в OpenGL, а приведенный выше код является лишь примером. В реальном коде я проверю, что тип 'sizeof' делится на тип, который я хочу передать OpenGL (в настоящее время плавающий), так что это должно облегчить проблемы с выравниванием. Что касается проблемы с использованием таких вещей, как 'std :: string' как типа, это действительно так, и я считаю, что если я утверждаю на' std :: is_trivially_copyable', это должно быть безопасным. На практике различными типами будут векторы разного размера. – dempzorz