C++ 在连续内存中存储任意元素
我试图创建一个数据结构,它将在连续内存中保存N个不同类型的数据。所以在编译时,我可以说我想存储3种不同类型的4个元素,在内存中看起来像111122223333 我一直在使用一种可变模板方法,我认为它可以满足我的需要,但是我不确定如何在add方法中将元素添加到每个数组中C++ 在连续内存中存储任意元素,c++,c++11,templates,variadic-templates,memory-alignment,C++,C++11,Templates,Variadic Templates,Memory Alignment,我试图创建一个数据结构,它将在连续内存中保存N个不同类型的数据。所以在编译时,我可以说我想存储3种不同类型的4个元素,在内存中看起来像111122223333 我一直在使用一种可变模板方法,我认为它可以满足我的需要,但是我不确定如何在add方法中将元素添加到每个数组中 template<std::size_t N, typename... Args> class Batch { private: std::tuple<std::array<Args
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;
}
模板
类批
{
私人:
std::元组数据;
大小\u t当前位置\u;
公众:
模板
void addToArray(标准::数组和数组,常量T和值)
{
数组[当前位置]=值;
}
无效添加(常量参数和…值)
{
//????
addToArray(/*数组,值*/);
currentPos_uz++;
}
常量void*数据()
{
&返回数据;
}
};
int main()
{
批次b;
b、 增加(1.0f,1.0,1);
b、 增加(2.0f,2.0,2);
b、 增加(3.0f、3.0、3);
b、 增加(4.0f、4.0、4);
返回0;
}
即使我让它工作,内存布局是否正确?有更好的方法吗?有两种词汇类型可以帮助您。
和 std::variant更适合您的预期用途 而不是像这样创建自己的类型:
Batched<3, float, double, int> b;
我认为这不是个好主意,但是。。。我展示它只是为了好玩 使用
std::vector
(以及由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;
}
在某些体系结构中,可能导致对未对齐地址中的浮点*
的访问,从而终止程序
从data()
返回的指针恢复值的正确(安全)方法是在toCoutHelper()中使用的方法,使用'std::memcpy()
谢谢,虽然这可能是一个浪费内存的问题。例如,如果我有std::vector,其中MyStruct包含5个成员,float的每个元素将占用5倍的内存,对吗?@dempzorz Yes。这是因为只有一个对齐方式和大小可以简化内存大小的调整。太棒了!我很确定这对我会有用。@ildjarn-我也有同样的恐惧,出于这个原因,我不鼓励这种(只是为了好玩)的解决方案。但是你认为哪一个指令是危险的?为什么?@ildjarn-不太明白。。。你认为这是危险的吗std::memcpy(buffer.data()+pos,&v0,sizeof(T))代码>?但是buffer.data()
(如果我没有错的话)是指向char
的指针memcpy()
也可能存在char
指针的内存分配问题?@ildjarn-关于非平凡可复制类型的好观点;我将修改我的答案以考虑这一点;谢谢但是(如果我没有错的话)一个std::string
不是一个alligment问题(我想t
valuev0
是t
alligen),而是一个内部引用计数器问题。@ildjarn-我明白了。。。好吧,你关于Batch::data()
(或者更好:错误使用返回指针)的危险性的观点是正确的;我很清楚,但并非所有读者都清楚。我将尝试在“编辑3”中解释(希望我的英语可以理解)。不管怎样,你认为我们对这个答案的评论如何?它们不会让偶尔的读者感到困惑吗?我们最好删除他们?根据Ildjarn指出的一个可能的问题修改了我的答案;简言之:如果在参数中…
不是POD类型,请不要使用我的解决方案一点说明我为什么问这个问题以及为什么接受答案,即使有警告。这最终将用于向OpenGL传递缓冲区,上面的代码只是一个示例。在实际代码中,我将检查类型的大小是否可以被我要传递给OpenGL的类型(当前为float)整除,这样可以减轻对齐问题。至于使用像std::string
这样的东西作为类型的问题,这是有效的,我相信如果我断言std::是可复制的,它应该是安全的。实际上,不同类型的向量大小不同。
vec.emplace_back(1); //int
vec.emplace_back(1.0f); //float
vec.emplace_back(1.0); //double
#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;
}
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f { *(float*)(pv+pos) }; // <-- risk of unaligned access
char const * pv = (char const *)b.data();
size_t pos = { /* some value here */ };
float f;
std::memcpy( & f, pv + pos, sizeof(f) );