Skip to content

File param_transform.hpp

File List > backends > cxx > include > zmbt > model > param_transform.hpp

Go to the documentation of this file

#ifndef ZMBT_MODEL_PARAM_TRANSFORM_HPP_
#define ZMBT_MODEL_PARAM_TRANSFORM_HPP_


#include <boost/json.hpp>
#include <zmbt/core/aliases.hpp>
#include <zmbt/core/generic_traits.hpp>
#include <zmbt/core/interface_id.hpp>
#include <zmbt/core/interface_traits.hpp>
#include <zmbt/core/object_id.hpp>
#include <zmbt/core/parameter.hpp>
#include <zmbt/model/exceptions.hpp>
#include <zmbt/reflect/serialization.hpp>
#include <stdint.h>
#include <utility>

#include "environment.hpp"
#include "traits.hpp"


namespace zmbt {


namespace detail {

template <class T, class = void>
struct param_transform;


struct param_transform_base
{
    Param const& param;
    boost::json::array const& pointers;

    struct Kind {
        constexpr static uint32_t none      = 0;
        constexpr static uint32_t trig_node = 0x1 << 0U;
        constexpr static uint32_t trig_obj  = 0x1 << 1U;
        constexpr static uint32_t trig_ifc  = 0x1 << 2U;
        constexpr static uint32_t ifc_node  = 0x1 << 3U;
        constexpr static uint32_t ifc_obj   = 0x1 << 4U;
        constexpr static uint32_t ifc_ifc   = 0x1 << 5U;
    };

    uint32_t pkind {};

    param_transform_base(Param const& p, boost::json::array const& ptrs);
};




template <class T>
struct param_transform<T, require_json_from<T>>
{
    param_transform(Param const&, boost::json::array const&) {}

    boost::json::value operator()(T&& arg)
    {
        return zmbt::json_from(arg);
    }
};



template <class T, class = void>
struct param_transform_interface;

template <class T>
struct param_transform_interface<T, require_cal<T>> : public param_transform_base
{
    using param_transform_base::param_transform_base;

    boost::json::value operator()(T&& arg)
    {
        Environment env {};
        env.RegisterPrototypes(arg);

        boost::json::value pvalue {};
        if ((Kind::trig_ifc & pkind) && (Kind::ifc_node & pkind) )
        {
            throw model_error("Ambiguous param %s (missing object on channel nodes?)", param);
        }
        if (Kind::trig_node & pkind)
        {
            pvalue = env.RegisterAnonymousTrigger(ifc_host_nullptr<T>, arg);
        }
        else if (Kind::trig_ifc & pkind)
        {
            pvalue = env.RegisterParametricTriggerIfc(arg);
        }
        else if (Kind::ifc_node & pkind)
        {
            pvalue = {
                {"obj", "$default"},
                {"ifc", interface_id{arg} },
            };
        }
        else if (Kind::ifc_ifc & pkind)
        {
            pvalue = interface_id{arg};
        }
        else if (Kind::trig_obj & pkind)
        {
            pvalue = register_trig_obj(arg);
        }
        else if (Kind::ifc_obj & pkind) // it could be am mfp on an object with call operator
        {
            pvalue = construct_or_default<object_id, T>(std::forward<T>(arg));
        }
        else
        {
            throw model_error("Ambiguous param %s (should it be a part of an interface?) `%s`", param, pointers);
        }
        return pvalue;
    }

    template <class O>
    enable_if_t<not ifc_is_fn_handle<O>::value, object_id>
    register_trig_obj(O&& obj)
    {
        return Environment().RegisterParametricTriggerObj(obj);
    }

    template <class O>
    enable_if_t<ifc_is_fn_handle<O>::value, object_id>
    register_trig_obj(O&&)
    {
        return object_id{};
    }
};

template <class T>
struct param_transform_interface<T, require_not_cal<T>> : public param_transform_base
{
    using param_transform_base::param_transform_base;

    boost::json::value operator()(T&& arg)
    {

        if (Kind::trig_obj & pkind)
        {
            return boost::json::value{Environment().RegisterParametricTriggerObj(arg)};
        }
        else if (Kind::ifc_obj & pkind)
        {
            return object_id{arg};
        }
        else
        {
            throw model_error("Ambiguous param %s (should it be a trigger or channel object?)", param);
        }
        return nullptr;
    }
};

template <class T>
struct param_transform<T, require_no_json_from<T>> : public param_transform_interface<T>
{
    using param_transform_interface<T>::param_transform_interface;
};

} // namespace detail


template <class T>
boost::json::value param_transform(Param const& param, boost::json::array const& pointers, T&& value)
{
    return detail::param_transform<T>(param, pointers)(std::forward<T>(value));
}

}


#endif