如何在 C++ 中使用 Boost 仅反序列化文件的一部分?
How to deserialize only parts of a file using Boost in C++?
我有许多使用 boost::archive::binary_oarchive
序列化的相同 class 的实例。它们按给定顺序保存。我只对加载其中一个感兴趣并且我知道它的位置。如何检索(反序列化)一个对象而不必反序列化几乎所有对象?
更一般地说,从文件中仅检索某些对象的最佳方法是什么?
现在,我的代码看起来像这样:
std::ofstream saveFile("savefile.save");
boost::archive::binary_oarchive oa(saveFile);
oa << arrayOfObjects;
saveFile.close();
// Later...
std::ifstream loadFile("savefile.save");
boost::archive::binary_iarchive ia(loadFile);
ia >> arrayOfObjects;
auto oneSpecificObject = arrayOfObjects[i]; // I have to do this; not efficient
loadFile.close();
提前致谢并干杯,
这完全取决于 arrayOfObjects
的确切类型。
因为那是事情如何序列化的决定性因素。
如果是真正的数组,事情可能不会太复杂。尽管一旦涉及到对象跟踪,它就会再次变得非常棘手。例如
X x{"the answer is 42"};
// std::vector arrayOfObject { &x, &x, &x, &x, &x, &x, &x, &x }; // OR:
X* arrayOfObject[] = { &x, &x, &x, &x, &x, &x, &x, &x };
{
boost::archive::text_oarchive oa(std::cout);
oa << arrayOfObject;
}
版画
22 serialization::archive 19 8 0 1 0
0 16 the answer is 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0
因此,天真地只读回第 6 个元素会导致……未指定的结果。因此,我的建议是/只读取整个数组/并丢弃所有不需要的数据。
破解它
如果我们不安全,假设没有上述的复杂因素并取决于实现细节(例如向量实际如何序列化),您可以编写反序列化来匹配并获得您希望的行为:
Live On Coliru - Don't Try This At Home
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/preprocessor.hpp>
#include <iostream>
#include <sstream>
#ifndef TYPE
#define TYPE xml
#endif
using oarchive = boost::archive::BOOST_PP_CAT(TYPE, _oarchive);
using iarchive = boost::archive::BOOST_PP_CAT(TYPE, _iarchive);
struct X {
std::string answer;
void serialize(auto& ar, unsigned) { ar& BOOST_SERIALIZATION_NVP(answer); }
};
template <typename T, size_t TargetIndex>
struct FakeVectorReader {
T element;
template <typename Ar> void serialize(Ar& ar, unsigned)
{
static_assert(typename Ar::is_loading{});
using namespace boost::serialization;
collection_size_type count;
ar >> make_nvp("count", count);
if (library_version_type(3) < ar.get_library_version()) {
item_version_type item_version(0);
ar >> make_nvp("item_version", item_version);
}
assert(count > TargetIndex);
T v;
for (size_t i = 0; i < count; ++i) {
ar >> make_nvp("item", v);
if (i == TargetIndex) {
element = std::move(v);
ar.reset_object_address(&element, &v); // a bit half-hearted, this
}
}
}
};
int main()
{
std::vector const arrayOfObject{
X{"zero"}, {"one"}, {"two"}, {"three"}, {"four"},
{"five"}, {"six"}, {"seven"}, {"eight"}, {"nine"},
};
std::stringstream ss;
{
oarchive oa(ss);
oa << BOOST_SERIALIZATION_NVP(arrayOfObject);
}
if (std::string("binary") != BOOST_PP_STRINGIZE(TYPE)) {
std::cout << ss.str() << std::endl;
}
{
iarchive ia(ss);
FakeVectorReader<X, 6> hack;
ia >> boost::serialization::make_nvp("arrayOfObject", hack);
std::cout << "hack.element: " << hack.element.answer << "\n";
}
}
打印
22 serialization::archive 19 0 0 10 0 0 0 4 zero 3 one 3 two 5 three 4 four 4 five 3 six 5 seven 5 eight 4 nine
hack.element: six
不要在家尝试这个
我相信你会明智地使用这些知识。
- 我深入研究了实现细节,
- 做了一个 half-hearted lip-service 对象跟踪,知道当对象在存档中实际别名时它会中断
- 请注意,如果您更改容器类型或
,所有这些都会中断
- 甚至升级到更新版本的 Boost 库。
- 请记住,所有元素实际上都是反序列化的(这样一来,如果数组后面有更多数据,那么至少您仍然可以读取它)。
- 如果
X
不安全就会泄漏(遵循Rule-Of-3/5/0)
我希望答案有助于说明为什么你不应该这样做,也许如果你以某种方式不能避免这种情况(你需要反序列化其中的一部分multi-terabyte存档,但不能再访问超级计算机了?)
我有许多使用 boost::archive::binary_oarchive
序列化的相同 class 的实例。它们按给定顺序保存。我只对加载其中一个感兴趣并且我知道它的位置。如何检索(反序列化)一个对象而不必反序列化几乎所有对象?
更一般地说,从文件中仅检索某些对象的最佳方法是什么?
现在,我的代码看起来像这样:
std::ofstream saveFile("savefile.save");
boost::archive::binary_oarchive oa(saveFile);
oa << arrayOfObjects;
saveFile.close();
// Later...
std::ifstream loadFile("savefile.save");
boost::archive::binary_iarchive ia(loadFile);
ia >> arrayOfObjects;
auto oneSpecificObject = arrayOfObjects[i]; // I have to do this; not efficient
loadFile.close();
提前致谢并干杯,
这完全取决于 arrayOfObjects
的确切类型。
因为那是事情如何序列化的决定性因素。
如果是真正的数组,事情可能不会太复杂。尽管一旦涉及到对象跟踪,它就会再次变得非常棘手。例如
X x{"the answer is 42"};
// std::vector arrayOfObject { &x, &x, &x, &x, &x, &x, &x, &x }; // OR:
X* arrayOfObject[] = { &x, &x, &x, &x, &x, &x, &x, &x };
{
boost::archive::text_oarchive oa(std::cout);
oa << arrayOfObject;
}
版画
22 serialization::archive 19 8 0 1 0
0 16 the answer is 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0
因此,天真地只读回第 6 个元素会导致……未指定的结果。因此,我的建议是/只读取整个数组/并丢弃所有不需要的数据。
破解它
如果我们不安全,假设没有上述的复杂因素并取决于实现细节(例如向量实际如何序列化),您可以编写反序列化来匹配并获得您希望的行为:
Live On Coliru - Don't Try This At Home
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/preprocessor.hpp>
#include <iostream>
#include <sstream>
#ifndef TYPE
#define TYPE xml
#endif
using oarchive = boost::archive::BOOST_PP_CAT(TYPE, _oarchive);
using iarchive = boost::archive::BOOST_PP_CAT(TYPE, _iarchive);
struct X {
std::string answer;
void serialize(auto& ar, unsigned) { ar& BOOST_SERIALIZATION_NVP(answer); }
};
template <typename T, size_t TargetIndex>
struct FakeVectorReader {
T element;
template <typename Ar> void serialize(Ar& ar, unsigned)
{
static_assert(typename Ar::is_loading{});
using namespace boost::serialization;
collection_size_type count;
ar >> make_nvp("count", count);
if (library_version_type(3) < ar.get_library_version()) {
item_version_type item_version(0);
ar >> make_nvp("item_version", item_version);
}
assert(count > TargetIndex);
T v;
for (size_t i = 0; i < count; ++i) {
ar >> make_nvp("item", v);
if (i == TargetIndex) {
element = std::move(v);
ar.reset_object_address(&element, &v); // a bit half-hearted, this
}
}
}
};
int main()
{
std::vector const arrayOfObject{
X{"zero"}, {"one"}, {"two"}, {"three"}, {"four"},
{"five"}, {"six"}, {"seven"}, {"eight"}, {"nine"},
};
std::stringstream ss;
{
oarchive oa(ss);
oa << BOOST_SERIALIZATION_NVP(arrayOfObject);
}
if (std::string("binary") != BOOST_PP_STRINGIZE(TYPE)) {
std::cout << ss.str() << std::endl;
}
{
iarchive ia(ss);
FakeVectorReader<X, 6> hack;
ia >> boost::serialization::make_nvp("arrayOfObject", hack);
std::cout << "hack.element: " << hack.element.answer << "\n";
}
}
打印
22 serialization::archive 19 0 0 10 0 0 0 4 zero 3 one 3 two 5 three 4 four 4 five 3 six 5 seven 5 eight 4 nine
hack.element: six
不要在家尝试这个
我相信你会明智地使用这些知识。
- 我深入研究了实现细节,
- 做了一个 half-hearted lip-service 对象跟踪,知道当对象在存档中实际别名时它会中断
- 请注意,如果您更改容器类型或 ,所有这些都会中断
- 甚至升级到更新版本的 Boost 库。
- 请记住,所有元素实际上都是反序列化的(这样一来,如果数组后面有更多数据,那么至少您仍然可以读取它)。
- 如果
X
不安全就会泄漏(遵循Rule-Of-3/5/0)
我希望答案有助于说明为什么你不应该这样做,也许如果你以某种方式不能避免这种情况(你需要反序列化其中的一部分multi-terabyte存档,但不能再访问超级计算机了?)