提问人:Tare 提问时间:9/6/2023 最后编辑:DailyLearnerTare 更新时间:9/6/2023 访问量:314
如何在 C++ 中仅将成员变量转换为字节数组?
How to turn only member variables into byte array in C++?
问:
TL;博士
我有一个结构体,不仅包含成员变量(例如,它们包含函数),而且我只想将成员变量转换为字节数组(/vector),这样我就可以在 Vulkan 中将数据上传到显卡。如何仅获取结构中表示成员变量的那部分?
我的具体方法
我有一个设置,我使用一个空结构并从中继承,...实际包含成员变量的结构。我使用它,这样我就可以在没有实际了解具体实现的情况下将成员留在一个班级中。ParamsBase
ParamsA
ParamsB
ParamsBase
Container
我想将类中的 Params 实例转换为字节缓冲区。Container
由于我需要实际//...struct,我在创建实例时为这些使用了通用子类,该子类允许我使用单个 getSize()
函数,而不是在每个子结构中覆盖它。ParamsA
ParamsB
// =============================
// === Define Params structs ===
// =============================
struct ParamsBase
{
virtual size_t getSize() const noexcept = 0;
};
struct ParamsA : public ParamsBase
{
float vec[3] = { 2.3f, 3.4f, 4.5f };
};
// ===============================================================
// === enable sizeof query for derived structs from ParamsBase ===
// ===============================================================
template<class T>
struct GetSizeImpl : T
{
using T::T;
size_t getSize() const noexcept final { return sizeof(T); }
};
template<class T, class... Args>
std::unique_ptr<T> create(Args&&... args)
{
return std::unique_ptr<T>(new GetSizeImpl<T>(std::forward<Args>(args)...));
}
// ============
// === Util ===
// ============
template<typename T>
std::vector<uint8_t> asByteVector(T* t, size_t size)
{
std::vector<uint8_t> byteVec;
byteVec.reserve(size);
uint8_t* dataArr = std::bit_cast<uint8_t*>(t);
byteVec.insert(byteVec.end(), &dataArr[0], &dataArr[size]);
return byteVec;
}
// ============================================
// === Use Params struct in container class ===
// ============================================
class Container
{
public:
Container(ParamsBase* params) : Params(params) {}
const std::vector<uint8_t>& getParamsAsBuffer()
{
ParamsAsBuffer = asByteVector(Params, Params->getSize());
return ParamsAsBuffer;
}
size_t getParamsSize() const { return Params->getSize(); }
private:
ParamsBase* Params = nullptr;
std::vector<uint8_t> ParamsAsBuffer;
};
使用所有这些,我的 Params 结构的大小太大,并且以包含一些垃圾 (?) 数据的两个字节开头。我认为它与函数有关,因为即使是带有函数的非模板化结构也存在此问题,但我不能确定这个假设是否正确。getSize()
这个小小的比较显示了我得到的和我想要的之间的差异:
// ================================
// === Define comparison struct ===
// ================================
struct ParamsCompareA
{
float vec[3] = { 2.3f, 3.4f, 4.5f };
};
int main()
{
// create instances
auto pA = create<ParamsA>();
Container cA(pA.get());
std::vector<uint8_t> vecA = cA.getParamsAsBuffer();
// comparison
ParamsCompareA pcA;
size_t sizeCompA = sizeof(ParamsCompareA);
std::vector<uint8_t> compVecA = asByteVector(&pcA, sizeof(ParamsCompareA));
std::cout << "ParamsA size: " << std::to_string(pA->getSize())
<< "; CompParamsA size: " << sizeof(ParamsCompareA) << std::endl;
float* bufAf = reinterpret_cast<float*>(vecA.data());
float* bufCompAf = reinterpret_cast<float*>(compVecA.data());
}
包含 12 个条目(即结构体有 12 个字节大),将它们重新解释为浮点数会显示正确的值。据报道有 24 个条目,我想要的实际数据是(从 0 开始)字节 2 到 14。compVecA
vecA
我可以对 2 的偏移量进行硬编码(与 相同),但我很确定这不是处理此问题的正确方法。那么,有没有办法只访问我想要的数据部分呢?sizeof(GetSizeImpl)
Param 子结构的目的是让用户尽可能轻松地添加自己的参数结构并将其上传到 Vulkan 缓冲区 (/Shader),即他们应该只关心他们实际需要的数据,我可以在其他地方完成所有处理和转换。
答:
通过添加一些约定和布局,这可以与您当前的设置一起完成。
第一个修改是在结构内声明结构内的成员变量。Param
struct ParamsA : public ParamsBase
{
struct Members {
float vec[3] = { 2.3f, 3.4f, 4.5f };
} m;
};
然后添加另一个虚拟函数ParamBase
virtual const void* data() const = 0;
现在我们可以改为 return,也可以覆盖其他函数GetSizeImpl::getSize
sizeof(T::Members)
const void* data() const override { return &this->T::m; }
现在,您既有正确的数据字节大小,又有指向 POD 数据的指针,在法律上可以随心所欲。memcpy
评论
不要从任何东西派生。有一个包装器类型,它提供每个参数类型之间共享的行为。ParamsA
struct ParamsA // No inheritance!
{
float vec[3] = { 2.3f, 3.4f, 4.5f };
};
struct BaseParameters {
virtual std::span<std::byte> as_bytes() = 0;
};
template <typename T>
struct Parameters : BaseParameters {
T data;
/// ... other data members as you need
std::span<std::byte> as_bytes() {
return std::span(&data, 1).as_bytes();
}
};
struct ParamsCompareA
{
float vec[3] = { 2.3f, 3.4f, 4.5f };
};
int main()
{
// create instances
auto * pA = new Parameters<ParamsA>{};
auto vecA = pA->as_bytes();
// comparison
ParamsCompareA pcA;
std::cout << "ParamsA size: " << std::to_string(vecA.size())
<< "; CompParamsA size: " << sizeof(ParamsCompareA) << std::endl;
float* bufAf = reinterpret_cast<float *>(vecA.data());
float* bufCompA = pcA.vec;
}
评论
struct BaseParameters { virtual std::span<const std::byte> as_bytes() = 0; };
template <typename T> struct Parameters : BaseParameters { T data; /// ... other data members as you need std::span<const std::byte> as_bytes() { return std::as_bytes(std::span(&data, 1)); } };
下一个:愚弄 R 的方法调度
评论
sizeof
std::memcpy
truct GetSizeImpl : T
std::vector<uint8_t> Params::serialize();
memcpy