繁体   English   中英

在 std::vector 中存储任意数据的方法<unsigned char>

[英]Ways to store arbitrary data in a std::vector<unsigned char>

我有一个第三方 api,它提供以下类型(大约)用于将任意数据附加到树结构中的节点:

// 3rd party, cannot be modified
struct AttachedData
{
    std::string key;
    std::vector<unsigned char> data;
};

我认为它使用std::vector<unsigned char>因为数据也可以写入磁盘,但这在我的用例中并不重要,因为数据仅在运行时保留。

有不同类型的对象必须存储在AttachedData (每个实例只有一个对象)。 因为我只在运行时需要数据,所以我没有费心实现任何类型的序列化(主要是因为不必要的开销)。 我决定使用所有可能类型的变体,并在AttachedData存储指向其实例的指针:

using StorageType = std::variant<EventArgs1, EventArgs2, SomeData /*, ...*/>;

AttachedData& attachedData = api.getAttachedData(targetNode, "some key"); // get the correct container
auto storage = new StorageType(someDataInstance); // somehow create the variant on the heap
attachedData.data.resize(sizeof(storage)); // make enough space
std::memcpy(attachedData.data.data(), &storage, sizeof(storage)); // store the pointer to the variant 

在代码库的不同位置读取和处理数据:

AttachedData& attachedData = api.getAttachedData(targetNode, "some key"); // get the correct container
StorageType* storage = nullptr;
std::memcpy(&storage, attachedData.data.data(), sizeof(storage)); // read the pointer

// process the stored data
if (auto* args1 = get_if<EventArgs1>(storage))
    // ...
else if (auto* args2 = get_if<EventArgs2>(storage))
    // ...
// ...

// clean up
attachedData.data.clear();
delete storage;

有没有更干净或更优雅的方法来做到这一点? 我还考虑过使用placement new 并直接在向量中构建变体,但这可能会导致对齐问题,而且我也看不到任何好处。 我认为std::variant是一个很好的方法,但我也愿意接受这方面的建议。

更新

此答案中提出的方法不适用于 OP,因为事实证明,存储在节点中的向量可能会在没有用户控制的情况下被复制、移动或破坏。 我暂时不会删除答案,因为下面的讨论澄清了一些重要的细节,可能对其他人有用。


原答案

我不知道我是否理解这个问题,但是您可以在向量中创建一个StorageType对象(即std::variant )。 你只需要调整向量的大小以适应里面的对象:

AttachedData& attachedData = api.getAttachedData(targetNode, "some key");
attachedData.data.resize(sizeof(StorageType));
StorageType* p_obj = new (attachedData.data.data()) StorageType{someDataInstance};

只要所有变体的替代方案都没有过度对齐,对齐应该没问题

然后,在后面的代码中,您可以获得对存储对象的访问权限,如下所示:

AttachedData& attachedData = api.getAttachedData(targetNode, "some key"); // get the correct container
StorageType* p_obj = std::launder(reinterpret_cast<StorageType*>(attachedData.data.data()));

请注意,您需要在某处通过调用析构函数手动销毁所有存储的对象,或者使用std::destroy_at更好。


至于你的尝试,它有几个问题。 例如,您不能将memcpy用于非平凡可复制类型的对象,或者您正在使用&storage ,它是指向内存中storage指针存储位置的指针(而不是它指向的指针)。

更新

根据您在评论中指定的其他详细信息,此方法将不起作用,因为您无法控制矢量的复制、移动或破坏。 在这种情况下,没有任何方法可以正常工作。 一旦你在向量中创建了一个对象,它的复制/移动/销毁将不会正确地涉及存储在其中的对象的复制/移动/销毁。 您的变体替代品是否可以轻松复制?

请注意,即使所有替代项都是, std::variant本身也不能保证在 C++17 中可以简单地复制 有一个针对这个问题建议

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM