Sure. The correct way to do this would be to implement your own archive type¹. This could be involved and I have no experience with it.
So, you can probably mimick it with a serialization wrapper with some limitations:
- I've not made de tag name configurable
- You might want to inject a target directory to contain the generated binary archives. Right now it just assumes the current working directory is going to make sense.
- object tracking for nested objects is out the window: because you write the wrapped objects to a new archive every time, it will always be the first time the object is seen by the tracking for that archive.
A Simple Wrapper
Here's the wrapper:
namespace uuids = boost::uuids;
template <typename T>
struct ExtBinaryFile {
using base = boost::serialization::nvp<T>;
ExtBinaryFile(T& ref) : ref_(ref) {}
private:
T& ref_;
friend boost::serialization::access;
template <typename Ar> void save(Ar& ar, unsigned) const {
auto src = random_name();
{
std::ofstream ofs(src, std::ios::binary);
boost::archive::binary_oarchive boa(ofs);
boa << ref_;
}
ar & BOOST_SERIALIZATION_NVP(src);
}
template <typename Ar> void load(Ar& ar, unsigned) {
std::string src;
ar & BOOST_SERIALIZATION_NVP(src);
{
std::ifstream ifs(src, std::ios::binary);
boost::archive::binary_iarchive bia(ifs);
bia >> ref_;
}
}
template <typename Ar> void serialize(Ar& ar, unsigned version) {
boost::serialization::split_member(ar, *this, version);
//if constexpr(Ar::is_saving::value) {
//this->save(ar, version);
//} else {
//this->load(ar, version);
//}
}
static std::string random_name() {
std::ostringstream oss;
std::mt19937 prng(std::random_device{}());
uuids::basic_random_generator<std::mt19937> gen{prng};
oss << gen() << ".dat";
return oss.str();
}
};
As you can see
- I paved over the problem of generating filenames by generating random-based UUIDs.
- The only thing that the XML will contain is a
src
element to contain the filename with the data.
Type Traits
As given the wrapper would not work because it violates assumptions made by the library regarding object identity. Let's tweak some serialization traits to fix that:
namespace boost { namespace serialization {
template <typename T> struct is_wrapper<ExtBinaryFile<T> > : std::true_type {};
template <typename T> struct tracking_level<ExtBinaryFile<T> > {
static const tracking_type value = tracking_type::track_never;
};
// const versions for completeness
template <typename T> struct is_wrapper<const ExtBinaryFile<T> > : is_wrapper<ExtBinaryFile<T> > {};
template <typename T> struct tracking_level<const ExtBinaryFile<T> > : tracking_level<ExtBinaryFile<T> > {};
} }
There. Now we don't accidentally think the identity of the wrapper is important.
Demo
class MyClass {
public:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, unsigned) {
auto dat = make_ext_binary(b);
ar & BOOST_SERIALIZATION_NVP(a)
& BOOST_SERIALIZATION_NVP(dat)
;
}
static void Save(const MyClass& inst, const std::string& filename) {
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(inst);
}
static void Load(MyClass& inst, const std::string& filename) {
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(inst);
}
//private:
int a = 0;
std::vector<int> b;
};
int main() {
{
MyClass orig;
orig.a = 99;
std::generate_n(back_inserter(orig.b), 1024, ::rand);
MyClass::Save(orig, "here.xml");
}
{
MyClass roundtrip;
MyClass::Load(roundtrip, "here.xml");
std::cout << "a: " << roundtrip.a << " b:" << roundtrip.b.size() << " elements\n";
}
}
As you can see I removed the private:
for a second so we can prove that the deserialized instance roundtripped correctly.
See It Live On Coliru
g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization
./a.out; ls -ltrah here.xml *.dat
a: 99 b:1024 elements
-rw-r--r-- 1 2001 2000 365 Jun 9 18:03 here.xml
-rw-r--r-- 1 2001 2000 4.1K Jun 9 18:03 55a89355-2637-42e4-b285-9846b046485e.dat
here.xml
:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="18">
<inst class_id="0" tracking_level="0" version="0">
<a>99</a>
<dat class_id="1" tracking_level="0" version="0">
<src>e783d3b6-0b23-4fad-be2c-97be9b1e0575.dat</src>
</dat>
</inst>
</boost_serialization>
Full Code
Live On Coliru
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <random>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
namespace uuids = boost::uuids;
template <typename T>
struct ExtBinaryFile {
using base = boost::serialization::nvp<T>;
ExtBinaryFile(T& ref) : ref_(ref) {}
private:
T& ref_;
friend boost::serialization::access;
template <typename Ar> void save(Ar& ar, unsigned) const {
auto src = random_name();
{
std::ofstream ofs(src, std::ios::binary);
boost::archive::binary_oarchive boa(ofs);
boa << ref_;
}
ar & BOOST_SERIALIZATION_NVP(src);
}
template <typename Ar> void load(Ar& ar, unsigned) {
std::string src;
ar & BOOST_SERIALIZATION_NVP(src);
{
std::ifstream ifs(src, std::ios::binary);
boost::archive::binary_iarchive bia(ifs);
bia >> ref_;
}
}
template <typename Ar> void serialize(Ar& ar, unsigned version) {
boost::serialization::split_member(ar, *this, version);
//if constexpr(Ar::is_saving::value) {
//this->save(ar, version);
//} else {
//this->load(ar, version);
//}
}
static std::string random_name() {
std::ostringstream oss;
std::mt19937 prng(std::random_device{}());
uuids::basic_random_generator<std::mt19937> gen{prng};
oss << gen() << ".dat";
return oss.str();
}
};
namespace boost { namespace serialization {
template <typename T> struct is_wrapper<ExtBinaryFile<T> > : std::true_type {};
template <typename T> struct tracking_level<ExtBinaryFile<T> > {
static const tracking_type value = tracking_type::track_never;
};
// const versions for completeness
template <typename T> struct is_wrapper<const ExtBinaryFile<T> > : is_wrapper<ExtBinaryFile<T> > {};
template <typename T> struct tracking_level<const ExtBinaryFile<T> > : tracking_level<ExtBinaryFile<T> > {};
} }
template <typename T>
static inline ExtBinaryFile<T> const make_ext_binary(T& ref) {
return {ref};
}
class MyClass {
public:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, unsigned) {
auto dat = make_ext_binary(b);
ar & BOOST_SERIALIZATION_NVP(a)
& BOOST_SERIALIZATION_NVP(dat)
;
}
static void Save(const MyClass& inst, const std::string& filename) {
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(inst);
}
static void Load(MyClass& inst, const std::string& filename) {
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(inst);
}
//private:
int a = 0;
std::vector<int> b;
};
#include <iostream>
int main() {
{
MyClass orig;
orig.a = 99;
std::generate_n(back_inserter(orig.b), 1024, ::rand);
MyClass::Save(orig, "here.xml");
}
{
MyClass roundtrip;
MyClass::Load(roundtrip, "here.xml");
std::cout << "a: " << roundtrip.a << " b:" << roundtrip.b.size() << " elements\n";
}
}
¹ Creating own implementation of Boost::Archive