Boost Serialize处理void指针二进制数据及TaskResults多态类序列化问询
Alright, let's break down your two Boost Serialization questions—they’re both common pain points when working with polymorphic types and raw binary data, so I’ll walk you through each solution clearly.
void* with Boost Serialization Boost Serialize doesn’t natively support void* because it lacks type information to know how to process the data. The workaround is to explicitly serialize two things: the length of the binary blob, then the raw bytes themselves. Here’s how to implement this:
Core Approach
- First serialize the
size_tlength value (so deserialization knows how much memory to allocate). - Use
boost::serialization::binary_objectto treat thevoid*memory as a raw byte stream, skipping type checks.
Example Implementation
#include <boost/serialization/binary_object.hpp> #include <boost/serialization/access.hpp> #include <cstdlib> // for malloc/free struct RawBinaryHolder { void* _data; size_t _dataLength; RawBinaryHolder(void* data = nullptr, size_t len = 0) : _data(data), _dataLength(len) {} ~RawBinaryHolder() { free(_data); // Clean up allocated memory } private: friend class boost::serialization::access; template<class Archive> void save(Archive& ar, const unsigned int version) const { // Save the length first ar << _dataLength; // Save the raw binary data ar << boost::serialization::make_binary_object(_data, _dataLength); } template<class Archive> void load(Archive& ar, const unsigned int version) { // Load the length to know how much memory to allocate ar >> _dataLength; // Allocate memory (use smart pointers if you want safer management) _data = malloc(_dataLength); // Load the raw bytes into the allocated memory ar >> boost::serialization::make_binary_object(_data, _dataLength); } // Split save/load requires this macro to tell Boost about the separate methods BOOST_SERIALIZATION_SPLIT_MEMBER() };
Key Notes
- Memory Management: You’re responsible for allocating/freeing memory during deserialization—use smart pointers or containers if you want to avoid manual leaks.
- Binary Blob Handling:
make_binary_objecttells the archive to treat the memory as raw bytes, so no type validation occurs here.
TaskResults Hierarchy Your current setup has a critical quirk: the _data pointer in TaskResults points to internal storage of _specificData in the derived class. Serializing the raw _data pointer directly will result in invalid memory addresses during deserialization (since the new vector will live in a different spot). Instead, we need to serialize the actual vector data, then re-link the _data pointer after deserialization.
Step 1: Fix the Base Class
Boost Serialization requires polymorphic classes to have a virtual destructor. Also, we’ll adjust SetData to be protected so derived classes can access it:
class TaskResults { public: void* _data; size_t _dataLength; // Add virtual destructor for polymorphic support virtual ~TaskResults() = default; protected: void SetData(void* memAddr, size_t len) { _data = memAddr; _dataLength = len; } private: friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int version) { // We don't serialize _data directly here—let the derived class handle the actual data // (The length is redundant since the derived class's vector holds all necessary info) } };
Step 2: Implement Derived Class Serialization
We’ll serialize the _specificData vector, then update the base class’s _data pointer after deserialization. Also, we’ll fix a bug in your original GiveData function (you forgot to multiply by sizeof(float) for byte length):
#include <boost/serialization/vector.hpp> #include <boost/serialization/export.hpp> class SpecificTaskResults : public TaskResults { public: std::vector<float> _specificData; void GiveData(std::vector<float> computedData) { _specificData = std::move(computedData); // Fix: _dataLength should be in bytes, not number of elements SetData(_specificData.data(), _specificData.size() * sizeof(float)); } private: friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int version) { // Serialize the base class first ar & boost::serialization::base_object<TaskResults>(*this); // Serialize the actual vector data ar & _specificData; // After deserialization, re-link the base class's _data pointer if constexpr (Archive::is_loading::value) { if (!_specificData.empty()) { SetData(_specificData.data(), _specificData.size() * sizeof(float)); } else { SetData(nullptr, 0); } } } }; // Register the derived class with Boost so it recognizes polymorphism BOOST_CLASS_EXPORT_KEY(SpecificTaskResults)
Step 3: Test the Serialization
Here’s how you’d use this in practice with binary archives:
#include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_iarchive.hpp> #include <fstream> #include <cassert> int main() { // Create and populate results SpecificTaskResults results; std::vector<float> sampleData = {1.5f, 2.7f, 3.9f}; results.GiveData(std::move(sampleData)); // Serialize to file std::ofstream ofs("task_results.bin", std::ios::binary); boost::archive::binary_oarchive oa(ofs); TaskResults* ptr = &results; // Polymorphic pointer oa << ptr; // Deserialize back std::ifstream ifs("task_results.bin", std::ios::binary); boost::archive::binary_iarchive ia(ifs); TaskResults* deserializedPtr = nullptr; ia >> deserializedPtr; // Verify the data SpecificTaskResults* deserializedResults = dynamic_cast<SpecificTaskResults*>(deserializedPtr); if (deserializedResults) { assert(deserializedResults->_specificData == std::vector<float>{1.5f, 2.7f, 3.9f}); assert(deserializedResults->_data == deserializedResults->_specificData.data()); assert(deserializedResults->_dataLength == 3 * sizeof(float)); } // Clean up delete deserializedPtr; return 0; }
Key Notes
- Polymorphic Registration:
BOOST_CLASS_EXPORT_KEYtells Boost about the derived class, so it can handle serialization/deserialization through base class pointers. - Deserialization Linking: The
if constexprcheck ensures we only re-link the_datapointer during loading, not saving. - Byte Length Fix: Always use byte count for raw data pointers—your original code used element count, which would lead to incorrect memory size calculations.
内容的提问来源于stack exchange,提问作者PhilipV

