Enable exporting and importing subsimulator state
This is a follow-up to #765 and the second and final step to close #756. It is part of a PR series which will culminate in the implementation of #757, and until that point, I am targeting the dev/state-persistence branch rather than master.
Here, I've implemented functionality to export the internal state of individual subsimulators in a generic, structured form, and to import it again later.
This exported form is intended as an intermediate step before serialisation to disk. The idea was to create a type that can be inspected and serialised to almost any file format we'd like.
The type is defined by cosim::serialization::node in cosim/serialization.hpp. It is a hierarchical, dynamic data type with support for a variety of primitive scalar types and a few aggregate types: strings, arrays of nodes, dictionaries of nodes, and binary blobs. (Think JSON-like structure, only in-memory and with more types.)
Edit: node was originally a homebrew type. Now, it is based on Boost.PropertyTree. Otherwise, the description above still fits pretty well.
Hm. Seems like my little trick of storing an incomplete node type in an unordered_map doesn't work on GCC 9. It worked locally for me, but I use a more recent compiler. I've changed this PR to "draft" status while I figure out what to do about it.
Ok, I made it work now.
It seems that to make a recursive map-like data type in standard C++, one has to hide the internal type, for example by messing about with void* pointers. Rather than do this myself, I have replaced the homemade cosim::serialization::node class with an alias for boost::property_tree::basic_ptree. The downside of this is a slightly less elegant API, but this is more than outweighed by the confidence we get from using tried-and-true code. Plus, we're already using ptree elsewhere in libcosim, so there's no extra dependency baggage involved.