serialize/deserialize into/from msgpack.
#include <map>
#include <vector>
#include <string>
#include <wad/default_archiver.hpp>
#include <wad/interface.hpp>
#include <wad/in_place.hpp>
#include <wad/string.hpp>
#include <wad/vector.hpp>
#include <wad/map.hpp>
namespace user
{
struct defined
{
std::string name;
std::vector<double> values;
std::map<std::string, int> nums;
template<typename Archiver>
bool archive(Archiver& arc) const
{
return wad::archive<wad::type::map>(arc,
"name", name, "values", values, "nums", nums);
}
};
} // user
int main()
{
std::vector<user::defined> uds = {/* ... */};
wad::write_archive sink;
if(!wad::save(sink, uds)) {return 1;}
sink.dump("checkpoint.msg");
// ...
wad::read_archive src("checkpoint.msg");
if(!wad::load(src, uds)) {return 1;}
// ...
return 0;
}
The user-interface is strongly inspired by Boost.serialization and cereal.
- Motivation
- Supported Types
- User-defined Class
- Subclass
- Archiver Requirements
- References
- Licensing Terms
wad does not intend to be a general-purpose msgpack parser. It directly serializes your class into and deserializes your class from msgpack, without any internal representation.
wad does not plan to be a multi-format serializer. It uses msgpack only.
wad intends to be a simple, small library that enables you to serialize and deserialize your class into/from msgpack.
Basic types and STL classes listed below are supported. By including the header file, you can save/load it into/from msgpack.
types | header file |
---|---|
bool |
wad/boolean.hpp |
integers | wad/integer.hpp |
enums | wad/enum.hpp |
float and double |
wad/floating.hpp |
std::complex |
wad/complex.hpp |
std::string |
wad/string.hpp |
std::vector |
wad/vector.hpp |
std::array |
wad/array.hpp |
std::deque |
wad/deque.hpp |
std::list |
wad/list.hpp |
std::forward_list |
wad/forward_list.hpp |
std::valarray |
wad/valarray.hpp |
std::set |
wad/set.hpp |
std::unordered_set |
wad/unordered_set.hpp |
std::map |
wad/map.hpp |
std::unordered_map |
wad/unordered_map.hpp |
std::pair |
wad/pair.hpp |
std::tuple |
wad/tuple.hpp |
std::queue |
wad/queue.hpp |
std::unique_ptr |
wad/unique_ptr.hpp |
std::atomic |
wad/atomic.hpp |
std::bitset |
wad/bitset.hpp |
std::optional |
wad/optional.hpp |
RNGs (e.g. mt19937 ) |
wad/random.hpp |
user-defined classes | wad/interface.hpp |
By defining one of the following member or non-member functions, you can serialize your class to msgpack with wad.
wad::save(arc, x)
function finds your x.save(arc)
member method. The same
applies to wad::load
.
Because save
function does not change the state of *this
object, it must be
marked as const
. On the other hand, load
must not be marked as const
.
struct X
{
std::string a;
double b;
std::int32_t c;
template<typename Archiver>
bool save(Archiver& arc) const
{
return wad::save<wad::type::map>(arc, "a", a, "b", b, "c", c);
}
template<typename Archiver>
bool load(Archiver& arc)
{
return wad::load<wad::type::map>(arc, "a", a, "b", b, "c", c);
}
};
wad finds save
and load
function through argument dependent lookup (ADL).
If you define save
and load
function in your namespace
, wad calls them.
namespace foo {
struct X
{
std::string a;
double b;
std::int32_t c;
};
template<typename Archiver>
bool save(Archiver& arc, const X& x)
{
return wad::save<wad::type::map>(arc, "a", x.a, "b", x.b, "c", x.c);
}
template<typename Archiver>
bool load(Archiver& arc, X& x)
{
return wad::save<wad::type::map>(arc, "a", x.a, "b", x.b, "c", x.c);
}
} // namespace foo
In many cases, the save()
and load()
function looks similar.
archive
method works in both ways, save and load, depending on the archiver
passed.
Because it loads and changes the state of *this
object, it must not be
marked as const
.
struct X
{
std::string a;
double b;
std::int32_t c;
template<typename Archiver>
bool archive(Archiver& arc)
{
return wad::archive<wad::type::map>(arc, "a", a, "b", b, "c", c);
}
};
Currently, loading a polymorphic type through archive
method is not supported.
To save the state of the base class, pass base_of<Base>(this)
to wad::save
and load
function. It should be passed as the second argument, immediately
after archiver
.
struct Base
{
std::string s;
virtual ~Base() = default;
template<typename Arc>
bool save(Arc& arc) const
{
return wad::save<wad::type::map>(arc, "s", s);
}
template<typename Arc>
bool load(Arc& arc)
{
return wad::load<wad::type::map>(arc, "s", s);
}
};
struct Derived : Base
{
std::int32_t i;
double d;
~Derived() override = default;
template<typename Arc>
bool save(Arc& arc) const
{
return wad::save<wad::type::map>(arc,
wad::base_of<Base>(this), "i", i, "d", d);
}
template<typename Arc>
bool load(Arc& arc)
{
return wad::load<wad::type::map>(arc,
wad::base_of<Base>(this), "i", i, "d", d);
}
};
To load Derived
through Base
, you need to tell the relationship to the
polymorphic loader and define the name of the derived type.
To achieve that, specialize the following class.
namespace wad {
template<>
struct register_subclass<extlib::Base, extlib::Derived>
{
static constexpr const char* name() noexcept {return "extlib::Derived";}
static const registered<extlib::Base, extlib::Derived> bound;
};
const registered<extlib::Base, extlib::Derived>
register_subclass<extlib::Base, extlib::Derived>::bound;
} /* wad */
You can use your own archive class in wad::save
, wad::load
, wad::archive
functions.
Note: wad::archive(arc, ...)
function checks if arc
has sink()
or src()
method and dispatch save(arc, ...)
or load(arc, ...)
depending on the
provided member method. If arc
supports both, the overload resolution becomes
ambiguous. To use wad::archive
method with your archiver, it should have only
one of those member methods.
class write_archiver
{
public:
// return type of sink.
using sink_iterator = Iterator;
// Return an iterator through which the binary will be written.
// iterator value_type should be a `char`.
Iterator sink() noexcept;
// Check if we can write some bytes in to the buffer.
bool is_writable(const std::size_t) const noexcept;
// Advance the internal pointer.
void advance(const std::size_t) noexcept;
// Retrace the internal pointer.
void retrace(const std::size_t) noexcept;
// Save the current location
Location npos() const noexcept;
// restore the status to the passed location
void seek(const Location&) noexcept;
};
class read_archiver
{
public:
// return type of sink.
using src_iterator = Iterator;
// Return an iterator through which the binary will be read.
// iterator value_type should be a `char`.
Iterator src() const noexcept;
// Check if we can read some bytes in to the buffer.
bool is_readable(const std::size_t) const noexcept;
// Advance the internal pointer.
void advance(const std::size_t) noexcept;
// Retrace the internal pointer.
void retrace(const std::size_t) noexcept;
// Save the current location
Location npos() const noexcept;
// Restore the status to the passed location
void seek(const Location&) noexcept;
};
To use your archiver with a polymorphic class, you need to bind them explicitly
to subclass relationships. By specifying your archiver as type arguments of
bind_archivers
that will be passed to the registere_subclass::bound
, the
polymorphic loader will recognize your archiver.
namespace wad {
template<>
struct register_subclass<extlib::Base, extlib::Derived>
{
static constexpr const char* name() noexcept {return "extlib::Derived";}
static const registered<extlib::Base, extlib::Derived> bound;
};
const registered<extlib::Base, extlib::Derived>
register_subclass<extlib::Base, extlib::Derived>::bound(
bind_archivers<your_write_archiver, your_read_archiver>{});
} /* wad */
This product is licensed under the terms of the MIT License.
- Copyright (c) 2020 Toru Niina
All rights reserved.