cronch

Backends

All classes in this section satisfy concepts::backend unless otherwise specified

JSON

class nloh

Provides json support via nlohammn/json

Supported types

Since nlohmann/json already has its own mechanism for parsing user-defined types, cronch leverages that first before falling back to its own metadata. If a type is not known to cronch and there is no nlohmann/json support for it then it will cause a compile error, unless it satisfies concepts::iterable, in which case it will be iterated over with the nlohmann::json instance being treated as an array.

Supported formats

This backend supports any json supported by nlohmann/json, in addition to any binary formats it supports (currently cbor, msgpack, bson, and ubjson). Support for the binary formats is done via the nloh::nloh() overload which takes a nlohmann::json (allowing stuff such as nlohmann::json::from_bson to be passed) and the serialize() overload which takes a Backend::document_type&, allowing the caller full access to the underlying nlohamnn::json.

Constructors

nloh::nloh(const std::string &contents)

Constructs the backend by parsing the contents string

nloh::nloh(nlohmann::json doc)

Constructs the backend by copying an existing parsed fragment

class boost

Provides json support via Boost.JSON

It is similar to the nlohmann backend, but due Boost.JSON’s lack of support for user-defined types, it adds its own system for conversion of them.

Supported types

All of Boost.JSON’s native types (object, array, string, etc). For additional type support, the converter struct is provided

template<typename T>
class converter

Provides a customisation point for user-defined type support. Specialise it to add support. Specialisations are provided for the following out of the box:

  • std::string

  • anything satisfying std::intergral

void to_json(boost::json::value &doc, const T &v)

Convert the type to a json value

void from_json(const boost::json::value &doc, T &v)

Convert the type from a json value

Example

#include <cronch/json/boost.hpp>

struct my_value_type {
    int i;
};

template<>
struct cronch::json::boost::converter<my_value_type> {

    void to_json(::boost::json::value& doc, const my_value_type& v) {
        doc.emplace_int64() = v.i;
    }
    void from_json(const ::boost::json::value& doc, my_value_type& v) {
        v.i = doc.as_int64();
    }
};

XML

class pugi

Provides xml support via pugixml

The default version uses a node-per-value format. With the name of each node being either the name of the type (if from a type without members metadata or the top level node) or the name of the member it references.

Supported types

pugixml does not provide any way of parsing user defined types automatically. Instead it uses purely const char* for its values. Therefore, in order to successfully wrap pugi, cronch uses boost::lexical_cast<std::string>() to serialize types and boost::lexical_cast<T>() to deserialize them. See the docs on lexical cast for more information.

Types that are neither supported by lexical cast or have members known to cronch will cause a compile error, unless the type is concepts::iterable, in which case it must have at least a name known by cronch (either a member name or type name).

XML structure

The output (and expected input) xml is of the form: <typename>{inner}</typename> where typename is the name of the type passed to pugi::serialize_to and {inner} is the result of serializing the inner members of that type. It will be one of the following (uses first matching, top to bottom):

Satisfied concepts

Output

has_members

<member name>{inner}</member name> for each member

ostreamable

Result of boost::lexical_cast<std::string>(v) where v is the value

iterable

<0..n>{inner}</0..n> for each element 0 to n where n is the size of the iterable - 1

Example

struct mytype {
   int i;
   std::vector<std::string> elements;
};
const auto myvalue = mytype{ .i = 2, .elements = { "Hello", "World" } };

Here myvalue would be formatted as:

<mytype>
    <i>2</i>
    <elements>
        <0>Hello</0>
        <1>World</1>
    </elements>
</mytype>

Constructors

pugi::pugi(const std::string &contents)

Constructs the backend by parsing the contents string

pugi::pugi(pugi::xml_document doc)

Constructs the backend by copying the reference to an existing xml document

Building your own

Making your own backend is mostly quite simple. Define a type which satisfies concepts::backend and use it. Done.

A very basic backend, which simply uses boost::lexical_cast to convert types, is shown below:


#include <cronch/deserialize.hpp>
#include <cronch/concepts.hpp> 
#include <cronch/metatypes.hpp> // Provides meta::nameof<int>() support i.e serializable<int> is satisfied

#include <boost/lexical_cast.hpp>

#include <iostream>


class plain_backend {
public:
    using document_type = std::string;

    explicit plain_backend(std::string doc) : document_{std::move(doc)} {}

    static void serialize_to(document_type& doc, const auto& v) {
        doc += boost::lexical_cast<std::string>(v);
    }

    template<typename V>
    void deserialize_to(V& v) const {
        v = boost::lexical_cast<std::decay_t<V>>(document_);
    }

    static auto to_string(const document_type& doc) -> std::string {
        return doc;
    }
private:
    std::string document_;


};

static_assert(cronch::concepts::backend<plain_backend>, "must be a backend");

int main(int argc, char** argv) {
    if (argc != 2) {
        std::cerr << "Invalid number of arguments, requires 1\n";
        return EXIT_FAILURE;
    }

    const auto v = cronch::deserialize<int>(plain_backend{argv[1]});

    std::cout << "You passed an int: " << v << '\n';
}