-->

Welcome to our Coding with python Page!!! hier you find various code with PHP, Python, AI, Cyber, etc ... Electricity, Energy, Nuclear Power

Tuesday 3 July 2018

C++ | JSON Serializer

Carrying on from:
All the code is available from git hub: ThorsSerializer but only reviewing a small section here.
The idea is that using this library it should be easy to define how a class is serialized without writing any code. See example in README
#ifndef THORSANVIL_SERIALIZE_JSON_SERIALIZE_H
#define THORSANVIL_SERIALIZE_JSON_SERIALIZE_H

/* Content:
 *
 * Used to define how a class is imported/exported via the Json Serialized
 *
 * Conventions:
 *      T:      The type of the base class
 *      I:      The type of the member
 *      MP:     The type of the member pointer
 *
 *      S:      Source type (std::ostream output) (ThorsAnvil::Json::ScannerSax input)
 *
 * Every type that is serializeable has a class JsonSerializeTraits.
 * For basic types (int/float etc) no definition is required and the default template version works
 * For compound types you need to define the type SerializeInfo.
 *      SerializeInfo:           It is a mpl vector of types.
 *                              Each type (in the vector) defines how to serialize
 *                              a member of the compound type.
 *
 * Boilerplate  code to create the appropriate types for SerializeInfo.
 *      #define THORSANVIL_SERIALIZE_JsonAttribute(className, member)
 *
 * Example:
 *      class MyClass
 *      {
 *          // STUFF
 *
 *          // If any members are private and need to be serialized then
 *          // JsonSerializeTraits<MyClass> must be a friend of the class so it can generate the appropriate code
 *
 *          friend class JsonSerializeTraits<MyClass>;
 *      };
 *
 *      namespace ThorsAnvil { namespace Serialize { namespace Json {
 *      template<>
 *      class JsonSerializeTraits<MyClass>
 *      {
 *          static ThorsAnvil::Serialize::Json::JsonSerializeType const  type    = Map;
 *
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member1);
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member2);
 *          THORSANVIL_SERIALIZE_JsonAttribute(MyClass, member3);
 *          typedef boost::mps::vector<member1, member2, member3>   SerializeInfo;
 *      };
 *      }}}
 *
 * Now we can serialize/deserialize with:
 *      std::cout << jsonExport(myClassObj) << "\n";
 *      std::cin  >> jsonInport(myClassObj);
 *
 * Same for std::vector
 *      std::vector<MyClass>  vec;  // Will serialize any fundamental type or type with JsonSerializeTraits<> specialized for it
 *                                  // If JsonSerializeTraits<> is not specialized for a compound type you get a compile time
 *                                  // error
 *      std::cout << jsonExport(vec) << "\n";
 *      std::cin  >> jsonImport(vec);
 */
#include "json/ScannerSax.h"
#include "json/ParserShiftReduce.tab.hpp"
#include <boost/mpl/at.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/typeof/typeof.hpp>
#include <iostream>


/*
 * Helper Macros:
 * 
 * These are macros that will build some boilerplate types needed by the serialization code.
 *
 * THORSANVIL_SERIALIZE_JsonAttribute:          This is the main macro used.
 *                                              It identifies a class member that will be serialized
 *
 * THORSANVIL_SERIALIZE_JsonAttribute_1:        Used internally (should probably not be used by others).
 * THORSANVIL_SERIALIZE_JsonAttributeAccess:    If you want to run some code to as part of the serialization processes
 *                                              this macro allows you to specify a type that will be used during serialization.
 *                                              Examples will be provided in the documentaion.
 *
 * THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess:  A generic accessor can be used to generate multiple items.
 *                                                      When de-serializing the Json can be applied to multiple elements.
 *                                                      Used manly for container classes like std::map
 *THORSANVIL_SERIALIZE_JsonGenericArrAttributeAccess:   A generic accessor used by for arrays rather than maps (std::vector)
 *                                                      But otherwise identical to THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess
 */
#define THORSANVIL_SERIALIZE_JsonAttribute(className, member)                                                                           \
    typedef BOOST_TYPEOF(((className*)01)->member)    JsonAttribute ## member ## Type;                                                  \
    THORSANVIL_SERIALIZE_JsonAttribute_1(className, member, JsonSerializeTraits<JsonAttribute ## member ## Type>)

#define THORSANVIL_SERIALIZE_JsonAttribute_1(className, member, SerTraits)                                                              \
    typedef BOOST_TYPEOF(&className::member)            JsonAttribute ## member ## TypePtr;                                             \
    typedef JsonSerialElementAccessor<className, JsonAttribute ## member ## TypePtr, SerTraits>  JsonAttribute ## member ## Accessor;   \
    struct member: JsonSerializeItem<className, JsonAttribute ## member ## Accessor, std::string>                                       \
    {                                                                                                                                   \
        member()                                                                                                                        \
            : JsonSerializeItem<className, JsonAttribute ## member ## Accessor, std::string>(#member, &className::member)               \
        {}                                                                                                                              \
    }
#define THORSANVIL_SERIALIZE_JsonAttributeAccess(className, member, accessor)                                                           \
    struct member: JsonSerializeItem<className, accessor, std::string>                                                                  \
    {                                                                                                                                   \
        member()                                                                                                                        \
            : JsonSerializeItem<className, accessor, std::string>(#member, accessor())                                                  \
        {}                                                                                                                              \
    }
#define THORSANVIL_SERIALIZE_JsonGenericMapAttributeAccess(className, accessor)                                                         \
    struct genericAccessor: JsonSerializeItem<className, accessor, std::string>                                                         \
    {                                                                                                                                   \
        genericAccessor()                                                                                                               \
            : JsonSerializeItem<className, accessor, int>(-1 , accessor())                                                              \
        {}                                                                                                                              \
    }



namespace ThorsAnvil
{
    namespace Serialize
    {
        /* External dependencies from the generic Serialization code */
        template<typename T, typename Parser>
        struct Importer;

        template<typename T, typename Printer>
        struct Exporter;

        namespace Json
        {

/* Three basic element types:   Invalid (this obejct is not a top level JSON object)
 *                              Map     A JSON object   { [<string> : <value> [, <string> : <value>]*]? }
 *                              Array   A JSON array    [ [<value> [, <value>]*]? ]
 */
enum JsonSerializeType {Invalid, Map, Array};

/*
 * All objects that want to be serialized by this code must define their own specialization of this class.
 * The default version will cause compilation errors. Which hopefully will bring the reader here.
 */
template<typename T>
struct JsonSerializeTraits
{
    static JsonSerializeType const  type    = Invalid;
    typedef T                     SerializeInfo;
};

// Forward declarations
template<typename T, typename A, typename RegisterKey>
struct JsonSerializeItem;

/* Class used by boost::mpl::for_each. Nothing special simple lamba's will replace them in the future */
/*
 *  T       The object Type
 *  S       The source type (parser or std::ostream)
 *
 * This is basically trying to templatize and remove the need for multiple action objects that
 * are called from mpl::for_each
 */
template<typename T, typename S>
struct MPLForEachActivateTrait;

template<typename T>
struct MPLForEachActivateTrait<T, std::ostream>
{
    typedef const T                 ObjectType;
    static int const                SerializeActionIndex    = 0;
};
template<typename T>
struct MPLForEachActivateTrait<T, ThorsAnvil::Json::ScannerSax>
{
    typedef T                       ObjectType;
    static int const                SerializeActionIndex    = 1;
};
template<typename T, typename S>
class MPLForEachActivateItem
{
    typedef          MPLForEachActivateTrait<T, S>  Traits;
    typedef typename Traits::ObjectType             ObjectType;

    S&              pump;
    ObjectType&     object;
    public:
        MPLForEachActivateItem(S& p, ObjectType& obj)
            : pump(p)
            , object(obj)
        {}
        // Depending on if the pump is a stream or a scanner
        // Calls      JsonSerialize::activate()
        // or         JsonDeSerialize::activate()
        template<typename SerializeItem>
        void operator()(SerializeItem const& item) const
        {
            typedef typename boost::mpl::at_c<typename SerializeItem::SerializeType, Traits::SerializeActionIndex>::type  SerializeAction;
            SerializeAction::activate(item, pump, object);
        }
};

/*
 * Objects of this type get stored in the
 *      JsonSerializeTraits::SerializeInfo
 *  This is what the user create with the macros above. The default A is JsonSerialElementAccessor
 *  But user can define their own action for complex objects This wrapper is merely a vehicle for
 *  calling the A methods in a controlled manner.
 *
 * Note: These classes are not designed to be used directly but via the macros:
 *          THORSANVIL_SERIALIZE_JsonAttribute
 *          THORSANVIL_SERIALIZE_JsonAttributeAccess
 *  See the notes by these macros for details
 */
template<typename T, typename A, typename RegisterKey, JsonSerializeType type = JsonSerializeTraits<T>::type>
struct JsonSerialize;


template<typename T, typename A, typename RegisterKey>
struct JsonSerialize<T, A, RegisterKey, Map>
{
    // Generic serialization of a JSON object
    static void activate(JsonSerializeItem<T, A, RegisterKey> const& item, std::ostream& stream, T const& src)
    {
        if (!item.first)
        {   stream << ',';
        }
        stream << '"' << item.memberName << '"' << ":";
        item.accessor.serialize(src, stream);
    }
};
template<typename C, typename A, typename RegisterKey>
struct JsonSerialize<C, A, RegisterKey, Array>
{
    // Generic serialization of a JSON array
    static void activate(JsonSerializeItem<C, A, RegisterKey> const& item, std::ostream& stream, C const& src)
    {
        if (!item.first)
        {   stream << ',';
        }
        item.accessor.serialize(src, stream);
    }
};

template<typename T, typename A,typename RegisterKey, JsonSerializeType type = JsonSerializeTraits<T>::type>
struct JsonDeSerialize;

template<typename T, typename A,typename RegisterKey>
struct JsonDeSerialize<T, A, RegisterKey, Map>
{
    static void activate(JsonSerializeItem<T, A, RegisterKey> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerAction(item.memberName, action);
    }
};
template<typename T, typename A>
struct JsonDeSerialize<T, A, int, Array>
{
    static void activate(JsonSerializeItem<T, A, int> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerActionOnAllArrItems(action);
    }
};

template<typename T, typename A>
struct JsonDeSerialize<T, A, std::string, Array>
{
    static void activate(JsonSerializeItem<T, A, std::string> const& item, ThorsAnvil::Json::ScannerSax& parser, T& dst)
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>    action(item.accessor.action(dst));
        parser.registerActionNext(action);
    }
};

/*
 * A type holder object that picks up the correct versions of JsonSerialize and JsonDeSerialize
 * Used by MPLForEachActivateItem to get the correct type
 */
template<typename T, typename A, typename RegisterKey>
struct JsonSerializeItem
{
    RegisterKey memberName;
    A           accessor;
    bool        first;

    JsonSerializeItem(RegisterKey const& name, A const& ac): memberName(name), accessor(ac), first(false) {}
    JsonSerializeItem& makeFirst() {first = true;return *this;}

    typedef    JsonSerialize<T,A,RegisterKey>       Serialize;
    typedef    JsonDeSerialize<T,A,RegisterKey>     DeSerialize;

    typedef boost::mpl::vector<Serialize, DeSerialize>      SerializeType;
};
/*
 * Importing
 * ============
 *
 * The JsonImportAction defines a class that is registered with a Json SAX parser
 * so that we can register callback actions for particular attributes.
 *
 * For fundamental types json is read directly into the value.
 * For compound types when the attribute is reached additional callbacks are registered
 * for each of the compound members that needs to be de-serialized (this is done recursively)
 * So we can de-serialize arbitrary json structures.
 */
template<typename SerializeInfo, typename I, bool EnablePod = boost::is_fundamental<I>::value>
class JsonImportAction: public ThorsAnvil::Json::SaxAction
{
    I&              memberRef;
    public:
        JsonImportAction(I& mr)
            : memberRef(mr)
        {}

        virtual void doPreAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&){}
        virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const& value)
        {
            // Read fundamental type directly into the member
            memberRef   = value.getValue<I>();
        }
};
template<typename SerializeInfo, typename I>
class JsonImportAction<SerializeInfo, I, false>: public ThorsAnvil::Json::SaxAction
{
    I&              memberRef;
    public:
        JsonImportAction(I& mr)
            : memberRef(mr)
        {}

        virtual void doAction(ThorsAnvil::Json::ScannerSax&, ThorsAnvil::Json::Key const&, JsonValue const&){}
        virtual void doPreAction(ThorsAnvil::Json::ScannerSax& parser, ThorsAnvil::Json::Key const&)
        {
            // Compound type. Register callback for each member.
            //                This is done when the attribute is reached in json not before
            boost::mpl::for_each<SerializeInfo>(MPLForEachActivateItem<I, ThorsAnvil::Json::ScannerSax>(parser, memberRef));
        }
};
/*
 * Need a function template to create the correct JsonImportAction()
 */
template<typename SerializeInfo, typename T, typename I>
ThorsAnvil::Json::SaxAction* new_JsonImportAction(T& dst, I T::* memberPtr)
{
    return new JsonImportAction<SerializeInfo, I>(dst.*memberPtr);
}


/* Default Serialization Traits:
 * Used by all types without their own specific serialization traits.
 */
template<JsonSerializeType>
struct JsonSerializeBrace
{
    static char braces[];
};

/*
 * The MemberScanner is used to register callbacks that will read sub-members from the json object
 */
template<typename T, typename MemberToSerialize = typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberScanner
{
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {
        boost::mpl::for_each<typename JsonSerializeTraits<T>::SerializeInfo>(MPLForEachActivateItem<T, ThorsAnvil::Json::ScannerSax>(scanner, destination));
    }
};
template<typename T>
struct MemberScanner<T, T>
{
    // A normal type with no SerializeInfo has no members thus no need to register callbacks.
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {}
};
template<typename T>
struct MemberScanner<T, void>
{
    // A normal type with no SerializeInfo has no members thus no need to register callbacks.
    void operator()(ThorsAnvil::Json::ScannerSax& scanner, T& destination)
    {}
};

/*
 * The MemberPrinter prints each member of an object.
 */
template<typename T, typename MemberToSerialize = typename JsonSerializeTraits<T>::SerializeInfo>
struct MemberPrinter
{
    void operator()(std::ostream& stream, T const& source)
    {
        typedef typename boost::mpl::at<typename JsonSerializeTraits<T>::SerializeInfo, boost::integral_constant<int,0> >::type  FirstType;
        typedef typename boost::mpl::pop_front<typename JsonSerializeTraits<T>::SerializeInfo>::type                             AllButFirstType;

        MPLForEachActivateItem<T, std::ostream>     itemPrinter(stream, source);


        // Print the first member (Call makeFirst() means no comma is printed)
        itemPrinter(FirstType().makeFirst());

        // For each of the other members use a loop a proceed each object with a comma
        boost::mpl::for_each<AllButFirstType>(itemPrinter);
    }
};
template<typename T>
struct MemberPrinter<T, T>
{
    // A normal object just prints itself.
    void operator()(std::ostream& stream, T const& source)
    {
        stream << source;
    }
};
template<typename T>
struct MemberPrinter<T, void>
{
    void operator()(std::ostream& stream, T const& source)
    {}
};


struct JsonSerializer
{
    template<typename T, JsonSerializeType base = JsonSerializeTraits<T>::type>
    struct Parser: ThorsAnvil::Json::ScannerSax
    {
        typedef boost::mpl::bool_<base != Invalid>      Parsable;
        Parser(T& dst)
            : destination(dst)
        {
            MemberScanner<T>    memberScanner;
            memberScanner(*this, destination);
        }

        void parse(std::istream& stream)
        {
            ScannerSax::parse<yy::ParserShiftReduce>(stream);
        }

        T&  destination;
    };

    template<typename T ,JsonSerializeType base = JsonSerializeTraits<T>::type>
    struct Printer
    {
        typedef boost::mpl::bool_<base != Invalid>      Printable;
        Printer(T const& src)
            : source(src)
        {}

        void print(std::ostream& stream)
        {
            stream << JsonSerializeBrace<base>::braces[0];

            MemberPrinter<T>    memberPrinter;
            memberPrinter(stream, source);

            stream << JsonSerializeBrace<base>::braces[1];
        }

        T const&    source;
    };

};

/*
 * Default accessors for fundamental types std::string
 * The serialize()  Recursively calls jsonInternalExport() on the member to serialize the member.
 *
 * The action()     Creates a JsonImportAction() that is registered with the SAX parser that just reads the
 *                  Item directly into the object. If the object is a compound type it uses the SerializeInfo
 *                  to register subsequent actions recursively so we always read directly into an object
 *                  not a copy.
 */
template<typename T, typename MP, typename SerTraits>
class JsonSerialElementAccessor
{
    MP          memberPtr;
    public:
    JsonSerialElementAccessor(MP mp): memberPtr(mp)    {}
    void serialize(T const& src, std::ostream& stream) const
    {
        stream << jsonInternalExport(src.*memberPtr);
    }
    std::auto_ptr<ThorsAnvil::Json::SaxAction>      action(T& dst) const
    {
        std::auto_ptr<ThorsAnvil::Json::SaxAction>  action(new_JsonImportAction<typename SerTraits::SerializeInfo>(dst, memberPtr));
        return action;
    }
};

/* Helper functions */
template<typename T>
Importer<T, typename JsonSerializer::template Parser<T> >   jsonInternalImport(T& object)
{
    return Importer<T, typename JsonSerializer::template Parser<T> >(object);
}

template<typename T>
Exporter<T, typename JsonSerializer::template Printer<T> >  jsonInternalExport(T const& object)
{
    return Exporter<T, typename JsonSerializer::template Printer<T> >(object);
}

        }
    }
}


#endif

No comments:

Post a Comment

Thanks for your comments

Rank

seo