I'm trying to use Boost serialization to serialize objects into a buffer. The objects are large (hundreds of MB) so I don't want to use binary_oarchive to serialize them into an std::stringstream to then copy-them into their final destination. I have a Buffer class which I would like to use instead.
The problem is, binary_oarchive takes an std::ostream as parameter, and this class's stream operator is not virtual, so I can't make my Buffer class inherit from it to be used by binary_oarchive. Similarly, I haven't found a way to inherit from binary_oarchive_impl that would let me use something else than std::ostream to serialize into.
So I looked into how to create an archive from scratch, here: https://www.boost.org/doc/libs/1_79_0/libs/serialization/doc/archive_reference.html, which I'm putting back here for reference:
#include <cstddef> // std::size_t
#include <boost/archive/detail/common_oarchive.hpp>
/////////////////////////////////////////////////////////////////////////
// class complete_oarchive
class complete_oarchive :
public boost::archive::detail::common_oarchive<complete_oarchive>
{
// permit serialization system privileged access to permit
// implementation of inline templates for maximum speed.
friend class boost::archive::save_access;
// member template for saving primitive types.
// Specialize for any types/templates that require special treatment
template<class T>
void save(T & t);
public:
//////////////////////////////////////////////////////////
// public interface used by programs that use the
// serialization library
// archives are expected to support this function
void save_binary(void *address, std::size_t count);
};
But unless I misunderstood something, it looks like by defining my archive this way, I need to overload the save method for every single type I want to store, in particular STL types, which kind of defies the point of using Boost serialization altogether. If I don't define save at all, I'm getting compilation errors indicating that a save function could not be found, in particular for things like std::string and for boost-specific types like boost::archive::version_type.
So my question is: how would you make it possible, with Boost serialization, to serialize in binary format into a custom Buffer object, while retaining all the power of Boost (i.e. not having to redefine how every single STL container and boost type is serialized)?
This is something I've done pretty easily in the past with the Cereal library, unfortunately I'm stuck with Boost for this particular code base.
Don't create your own archive class. Like the commenter said, use the streambuf interface to your advantage. The upside is that things will work for any of the archive implementations, including binary archives, and perhaps more interestingly things like the EOS Portable Archive implementation.
The streambuf interface can be quite flexible. E.g. i've used it to implement hashing/equality operations:
Generate operator== using Boost Serialization?
In that answer I used Boost Iostreams with its Device concept to make implementing things simpler.
Now if your Buffer type (which you might have shown) has an interface that resembles most buffers (i.e. one or more (void*,size) pairs), you could use existing adapters present in Boost IOstreams. E.g.
Boost: Re-using/clearing text_iarchive for de-serializing data from Asio:receive()
where I show how to use Serialization with a re-usable fixed buffer. Here's the Proof Of Concept:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <sstream>
namespace bar = boost::archive;
namespace bio = boost::iostreams;
struct Packet {
int i;
template <typename Ar> void serialize(Ar& ar, unsigned) { ar & i; }
};
namespace Reader {
template <typename T>
Packet deserialize(T const* data, size_t size) {
static_assert(boost::is_pod<T>::value , "T must be POD");
static_assert(boost::is_integral<T>::value, "T must be integral");
static_assert(sizeof(T) == sizeof(char) , "T must be byte-sized");
bio::stream<bio::array_source> stream(bio::array_source(data, size));
bar::text_iarchive ia(stream);
Packet result;
ia >> result;
return result;
}
template <typename T, size_t N>
Packet deserialize(T (&arr)[N]) {
return deserialize(arr, N);
}
template <typename T>
Packet deserialize(std::vector<T> const& v) {
return deserialize(v.data(), v.size());
}
template <typename T, size_t N>
Packet deserialize(boost::array<T, N> const& a) {
return deserialize(a.data(), a.size());
}
}
template <typename MutableBuffer>
void serialize(Packet const& data, MutableBuffer& buf)
{
bio::stream<bio::array_sink> s(buf.data(), buf.size());
bar::text_oarchive ar(s);
ar << data;
}
int main() {
boost::array<char, 1024> arr;
for (int i = 0; i < 100; ++i) {
serialize(Packet { i }, arr);
Packet roundtrip = Reader::deserialize(arr);
assert(roundtrip.i == i);
}
std::cout << "Done\n";
}
Related
Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.
Imagine a larger project, containing some parameter struct:
struct pars {
int foo;
};
With this struct as parameter, other functionality is implemented, e.g.:
// (de)serialization into different formats
static pars FromString(string const &text);
static string ToString(pars const &data);
static pars FromFile(string const &filename);
// [...]
// comparison / calculation / verification
static bool equals(pars l, pars r);
static pars average(pars a, pars b);
static bool isValid(pars p);
// [...]
// you-name-it
Now imagine a new member needs to be added to that struct:
struct pars {
int foo;
int bar; // new member
};
Is there a design pattern to break the build or issue warnings until all neccessary code places are adapted?
Example:
If I were to change int foo into string foo, I would not miss any code line which needs to be changed.
If int foo would need to change into unsigned int foo, I could rename foo to foo_u and have the compiler point me to where adaptations are neccessary.
One partial solution is to make the members private and settable only from the constructor, which has to be called with all parameters:
pars::pars(int _foo, int _bar)
: foo(_foo), bar(_bar)
{ }
This ensures the correct creation of pars, but not the usage - so this catches missing adaptations in FromString(), but not in ToString().
Unit tests would reveal such problems only during the test (I'm searching for a compile time method), and also only the (de)serialization part, and not that new bar is being considered everywhere (in the comparison / calculation / verification / ... functions as well).
Decouple the streaming operations from the source or destinations of the streams.
A very simple example:
#include <sstream>
#include <fstream>
struct pars
{
int foo;
int bar;
static constexpr auto current_version = 2;
};
std::istream &deserialise(std::istream &is, pars &model)
{
int version;
is >> version;
is >> model.foo;
if (version > 1) {
is >> model.bar;
}
return is;
}
std::ostream &serialise(std::ostream &os, const pars &model)
{
os << model.current_version << " ";
os << model.foo << " ";
// a version 2 addition
os << model.bar<< " ";
return os;
}
static pars FromString(std::string const &text)
{
std::istringstream iss(text);
auto result = pars();
deserialise(iss, result);
return result;
}
static std::string ToString(pars const &data)
{
std::ostringstream oss;
serialise(oss, data);
return oss.str();
}
static pars FromFile(std::string const &filename)
{
auto file = std::ifstream(filename);
auto result = pars();
deserialise(file, result);
return result;
}
Also have a look at:
boost.serialization http://www.boost.org/doc/libs/1_64_0/libs/serialization/doc/index.html
cereal https://github.com/USCiLab/cereal
etc.
A pattern that enforces this would be a for-each-member operation.
Pick a name, like members_of. Using ADL and a tag, make members_of(tag<T>) return a tuple of integral constant member pointers to the members of T.
This has to be written once. Then it can be used many spots.
I will write it in C++17 as in 14 and earlier it is just more verbose.
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<t> tag{};
template<auto X>using val_t=std::integral_constant<decltype(X), X>;
template<auto X>constexpr val_k<X> val{};
struct pars {
int foo;
friend constexpr auto members_of( tag_t<pars> ){
return std::make_tuple( val<&pars::foo> );
}
};
When you add a member you must also add it to the friend members_of.
template<class...Fs>
struct overload:Fs...{
using Fs::operator()...;
overload(Fs...fs):Fs(std::move(fs))... {}
};
overload lets you overload lambdas.
Finally write a foreach_tuple_element.
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
}
});
return retval;
}
when you add a new member bar to both pars and members_of, the above code breaks as the foreach cannot find an overload for val_t<&pars::bar>.
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
},
[&](val_t<&pars::bar>){
// code to handle pars.bar
}
});
return retval;
}
and now it would compile.
For serialization / deserialization specifically, you want a single method for both (where the type of one arg says if it is in or out), and string to/from is just a special case of serialization/deserialization.
template<class A, class Self,
std::enable_if_t<std::is_same<pars, std::decay_t<Self>>{}, int> =0
>
friend void Archive(A& a, Self& self) {
ArchiveBlock(a, archive_tag("pars"), 3, [&]{
Archive(a, self.foo);
Archive(a, self.bar);
});
}
this is an example of how a unified serialize/deserialize method (without the above member pointers) works. You override Archive on your output stream and on primitive const&, on your input stream and primitive&.
For almost everything else, you use common structure for both reading and writing from the Archive. This keeps the structure of your input and output identical.
ArchiveBlock( Archive&, tag, tag version, lambda ) wraps the lambda in whatever archiving block structure you have. As an example, your archive blocks might have length information in their header, allowing earlier deserializers to skip over added data at the end. It would also both read and write blocks; on writing, it would write out the block header and whatever else before writing the body (maybe keeping track of length and backing up to record length once they know it). On reading, it would ensure the tag exists (and deal with missing tags however you choose; skip?) and fast forward over newer block contents if you want to support older readers reading what newer writers write.
In more general cases where you need to keep code aligned with data this answer might solve things. Serialization and deserialization are very special cases, because unlike most bits of C++ code you have to future-proof the binary layout of everything. It is like writing library interfaces; there is lots more care required.
TLDR: I would like to transition a class serialisation from implementation level object_class_info to object_serializable, keeping compatibility with old data files while always writing files in the new format.
Rationale: When I started using the Boost Serialization library, I didn’t realise that it already came with a header (complex.hpp) to serialise std::complex<double>. Instead, I wrote my own, free-standing serialisation function:
namespace boost { namespace serialization {
template<class Archive, typename T>
void serialize(Archive& ar, std::complex<T>& comp, const unsigned int version) {
ar & reinterpret_cast<T(&)[2]>(comp)[0];
ar & reinterpret_cast<T(&)[2]>(comp)[1];
}
}
This by default enables version and class info tracking, which slows code down quite a bit. The serialization function which comes with Boost is a fair bit faster.
I would now like to transition to using the Boost version always when writing out new data files, but still be able to read in old data files. Reading in new files with an old binary is not an issue.
The problem is that the new serialisation is not versioned (obviously). Further, I don’t even see how I could attempt to read in an archive using the old version of the code and immediately write it out again using the new version, as the deserialisation/serialisation traits are global properties.
What would be the best way to either a) transparently read in old and new files while always writing new files or b) reading in an old file and immediately writing it out in the new format?
You can keep the old implementation and use it if the file version is "old".
Use the boost version of complex serialization only when saving or when the file version is "new".
This should be trivial if you have an object containing the complex data to serialize, because can bump the version on the containing object to achieve this
UPDATE
Sample, using a simple wrapper to invoke the old style of serialization:
Live On Coliru
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/complex.hpp>
template <typename T> struct old_format_wrapper {
T& wrapped;
old_format_wrapper(T& w) : wrapped(w) {}
};
template <typename T>
old_format_wrapper<T> old_format(T& w) { return {w}; }
namespace boost { namespace serialization {
template<class Archive, typename T>
void serialize(Archive& ar, old_format_wrapper<std::complex<T> >& comp, unsigned) {
ar & reinterpret_cast<T(&)[2]>(comp.wrapped)[0];
ar & reinterpret_cast<T(&)[2]>(comp.wrapped)[1];
}
} }
struct IHaveComplexData {
std::complex<double> data;
template <typename Ar> void serialize(Ar& ar, unsigned version) {
switch(version) {
case 0: { // old
auto wrap = old_format(data);
ar & wrap;
}
break;
case 1: // new
default:
ar & data; // uses boost serialization
break;
}
}
};
int main() {
{
boost::archive::text_oarchive oa(std::cout);
IHaveComplexData o { { 2, 33 } };
oa << o;
}
{
std::istringstream iss("22 serialization::archive 13 0 0 0 0 2.00000000000000000e+00 3.30000000000000000e+01");
boost::archive::text_iarchive ia(iss);
IHaveComplexData o;
ia >> o;
std::cout << o.data;
}
}
Prints (depending on your boost version):
22 serialization::archive 13 0 0 0 0 2.00000000000000000e+00 3.30000000000000000e+01
(2,33)
Of course, you can now set BOOST_CLASS_VERSION(IHaveComplexData, 1)
I've got a list of types which can be send over the network, take this example:
enum types {
E_T1,
E_T2,
E_T3,
E_T4
};
Now I have a list of classes which correspond to each of the types, let's say each is declared as class E_T1 {...}, class E_T2 {...}, etc.
They are not derived from a common base class and it's not possible to do so. Each of the classes has a verification method I need to invoke with the data send over the network. The client sends the data D and a id correspointing to the message type. I need to get hold of the object corresponding to the type. I can use C++0x features if needed.
What I've tried so far is using specialized templates for the types, holding a typedef for the object related to it. This was obviously a stupid idea as templates parameters need to be compile time constant so doing something along getType<data.id()>::type is not possible.
Then I tried using Boost.Variant to get a common returnable type like this (used mpl vector to iterate over the registered types at runntime for debbuging):
template <typename C>
struct getType() {
typedef C type;
}
typedef boost::mpl::vector<
getType<E_T1>,
getType<E_T2>,
getType<E_TX>...
> _types;
typedef boost::make_variant_over<_types>::type _type;
//use a map to store each type <-> id
boost::unorderd_map<types, _type> m;
m[E_T1] = getType<E_T1>();
m[data.id()]::type x; //<- access type, can now call x.validate(data)
The problem with this is that it's limited to 20 entries per variant per default. This can be overwritten but from what I understood the overhead per type should be considered and we are talking about a few thousand types here.
Also tried boost.any but it doesn't hold any type information so that's out of the question again. Has anyone any good ideas how this can be solved elegantly?
Looking for something where I don't have to write a 1k switch statement anytime I handle a type.
All types are nown at compile type, same goes for their corresponding IDs.
Id -> Type resolving needs to happen at runtime though.
Thanks in advance,
Robin.
External Polymorphism (*)
It's a widely known idiom, however it's widely used: I first encountered it in the shared_ptr implementation and it's been quite useful in my toolbox.
The idea is to actually create a base class for all those types. But not having them derive from it directly.
class Holder {
public:
virtual ~Holder() {}
virtual void verify(unsigned char const* bytes, size_t size) const = 0;
}; // class Holder
template <typename T>
class HolderT: public Holder {
public:
HolderT(): _t() {}
virtual void verify(unsigned char const* bytes, size_t size) const {
_t.verify();
}
private:
T _t;
}; // class HolderT
template <typename T>
std::unique_ptr<Holder> make_holder() {
return std::unique_ptr<Holder>(new HolderT<T>());
}
So, it's the classic strategy of adding a new level of indirection.
Now, you obviously do need a switch to move from value to class. Or perhaps... a map ?
using maker = std::unique_ptr<Holder> (&)();
using maker_map = std::unordered_map<types, maker>;
std::unique_ptr<Holder> select(types const E) {
static maker_map mm;
if (mm.empty()) {
mm.insert(std::make_pair(E_T1, make_holder<EC_T1>));
// ...
}
maker_map::const_iterator it = mm.find(E);
if (it == mm.end()) { return std::unique_ptr<Holder>(); }
return (*it->second)();
}
And now you can handle them polymorphically:
void verify(types const E, unsigned char const* bytes, size_t size) {
std::unique_ptr<Holder> holder = select(E);
if (not holder) { std::cerr << "Unknown type " << (int)E << "\n"; return; }
holder->verify(bytes, size);
}
Of course, you're welcome to make the strategy vary according to your needs. For example moving the map out of select so that you can register your types dynamically (like for plugins).
(*) At least that's the name I have for it, I would quite happy to find out it's already been named.
I'll assume you have a generic way of handling a message, such as for example an overloaded function:
void handle_message(const E_T1& msg);
void handle_message(const E_T2& msg);
//...
Now, you do not really need to get the object's type. All you need is a way to handle a message of that type, given the undecoded message.
So, I recommend you populate a map of factory functions:
std::unordered_map<types, std::function<void (unsigned char const* bytes, size_t size)> handlers;
handlers[E_E1] = [](unsigned char const* bytes, size_t size) { handle_message(E_T1(bytes, size)); };
// ...
Then, once you've decoded the type, you can use handlers[type](bytes, size) to decode and handle a message.
Try variadic templates and your already defined getType class:
enum types { T1_ID, T2_ID, .... };
class T1; class T2; class T3; ....
template <types t> struct getType;
template <> struct getType<T1_ID> { typedef T1 type; };
template <> struct getType<T2_ID> { typedef T2 type; };
...
And the operation verify:
template <types...>
struct type_operation;
template <types t1, types... rest>
struct type_operation<t1, rest...>
{
void verify(types t)
{
if (t == t1)
{
typename getType<t1>::type a;
a.verify(); // read from network and verify the rest of data....
}
else type_operation<rest...>::verify(t, data);
}
};
template <>
struct type_operation<>
{
void verify(types t)
{
ostringstream log; log << "not suppoted: " << t;
throw std::runtime_error(log.str()); //
}
};
Usage:
typedef type_operation<T1_ID, T2_ID, T3_ID, ,,.., TN_ID> type_mapping;
types id;
readFromNetwork(id);
type_mapping::verify(id);
As far as I understand, there is no serialization (boost::serialization, actually) support for boost::any placeholder.
Does someone know if there is a way to serialize a custom boost::any entity?
The problem here is obvious: boost::any uses template-based placeholders to store objects and typeid to check if boost::any_cast is appropriate.
So, there is a custom abstract superclass placeholder and custom template-based derived classes, which are created the following way:
template <T> custom_placeholder : public placeholder {
virtual std::type_info type() const { return typeid(T); }
virtual ...
};
Obviously, this brings some troubles when even thinking about serializing this stuff. Maybe someone knows some trick to make such kind of serialization (and of course, proper deserialization)?
Thank you
If you want to stick with boost::any i am not sure but you can write your own "boost::any". I'm using this code for proxy methods to pass the parameters.
#include <iostream>
#include <boost\smart_ptr\scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <sstream>
class my_placeholder
{
public:
virtual ~my_placeholder(){}
my_placeholder(){}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
//ar & m_placeholder;
}
};
template<typename T>
class my_derivedplaceholder:
public my_placeholder
{
public:
my_derivedplaceholder()
{
}
my_derivedplaceholder(T &value)
{
m_value=value;
}
T m_value;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
ar & boost::serialization::base_object<my_placeholder>(*this);
ar & m_value;
}
};
BOOST_CLASS_EXPORT_GUID(my_derivedplaceholder<int>, "p<int>");
class my_any
{
public:
my_any()
{
}
template<typename T>
my_any(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
template<typename T>
void operator=(const T &value)
{
m_placeholder.reset(new my_derivedplaceholder<T>(const_cast<T&>(value)));
}
protected:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
//ar & boost::serialization::base_object<bus_stop>(*this);
ar & m_placeholder;
}
template<typename T>
friend T my_anycast(my_any &val);
boost::shared_ptr<my_placeholder> m_placeholder;
};
template<typename T>
T my_anycast(my_any &val)
{
boost::shared_ptr<my_derivedplaceholder<T>> concrete=boost::dynamic_pointer_cast<my_derivedplaceholder<T>>(val.m_placeholder);
if (concrete.get()==NULL)
throw std::invalid_argument("Not convertible");
return concrete->m_value;
}
void main()
{
my_any m=10;
int a=my_anycast<int>(m);
std::cout << a << std::endl;
std::stringstream ss,ss2;
boost::archive::text_oarchive oa(ss);
oa << m;
boost::archive::text_iarchive ia(ss);
my_any m2;
ia >> m2;
std::cout << my_anycast<int>(m2) << std::endl;
}
It is not possible at all, at least for arbitrary types. Note that maybe you could serialize using some tricky code (like finding the size of the elements contained in the any), but the any code relies on the compiler statically putting the any type_code and the proper types inside the placeholder. You surely cannot do that in deserialization in C++, as the type that you'd get from the deserialization is not known at compile time (as required by the newly formed boost::any).
The best solution is to build some kind of specialized any type for the exact types of elements you're going to serialize. Then, you can have special cases for the actual type of element being deserialized, but note that each element type serialization/deserialization has to be phisically written as static C++ code.
PD. Some others suggested using boost::variant as a representation of this specialized type holding the exact types you're going to serialize. You need a way of discerning the exact type on deserialization, though (maybe assigning identifiers to types in the variant).
Assuming you have to use boost::any and you cannot switch to variant, a map<type_info const*, string(*)(any)> based solution could get you done.
You have to initialize at runtime such a map with all the types you plan to use. Of course, you can use something along the lines of
template <typename T>
struct any_serializer
{
static string perform(any a)
{
T const& x = any_cast<T const&>(a);
stringstream out;
out << x;
return out.str();
}
};
and populate the map with addresses of any_serializer<T>::perform under the key &typeid(T). You can specialize the class any_serializer and use some (ugly) macros to populate the map.
More difficult is of course the deserialization. I haven't had a look at boost::lexical_cast for a while, perhaps it can provide some help. I am afraid that this is totally problem-dependant. However, you only need one function, which takes a string and returns one any. You may also want to prepend your output string with a custom type identifier.
There is no need to create new class. Try to use xany https://sourceforge.net/projects/extendableany/?source=directory
xany class allows to add new methods to any's existing functionality. By the way there is a example in documentation which does exactly what you want.