如何在C++中仅将成员变量转换为字节数组?

Tar*_*are 6 c++ struct sizeof

长话短说

我有一个不仅仅是成员变量(例如它们包含函数)的结构,我只想将成员变量转换为字节数组(/向量),这样我就可以将数据上传到Vulkan中的显卡。如何仅获取结构体中代表成员变量的部分?

我的具体做法

我有一个设置,我使用一个空ParamsBase结构,并从中继承ParamsAParamsB...实际包含成员变量的结构。我使用它,这样我就可以将成员保留ParamsBase在一个Container类中,而无需实际了解具体实现。

我想将类中的 Params 实例转换Container为字节缓冲区。

ParamsA由于我需要实际/ /... 结构的大小ParamsB,因此在创建实例时我使用了一个通用子类,该子类允许我使用单个getSize()函数,而不是在每个 substruct 中覆盖它

// =============================
// === 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;
};
Run Code Online (Sandbox Code Playgroud)

使用所有这些,我的 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());
}
Run Code Online (Sandbox Code Playgroud)

包含compVecA12 个条目(即结构体有 12 个字节大),将它们重新解释为浮点数显示正确的值。据vecA报告有 24 个条目,我想要的实际数据位于(基于 0)字节 2 到 14。

我可以硬编码偏移量 2(与 相同sizeof(GetSizeImpl)),但我很确定这不是处理此问题的正确方法。那么,有没有办法可以只获取我想要的数据部分呢?

Param 子结构的目的是让用户尽可能轻松地添加自己的参数结构并将其上传到 Vulkan 缓冲区(/Shader),即他们应该只关心他们实际需要的数据,我可以在其他地方进行所有处理和转换。

Cal*_*eth 2

不要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;
}
Run Code Online (Sandbox Code Playgroud)