Skip to content

File trigger.hpp

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

Go to the documentation of this file

#ifndef ZMBT_MODEL_TRIGGER_HPP_
#define ZMBT_MODEL_TRIGGER_HPP_

#include "zmbt/model/exceptions.hpp"
#include "zmbt/core.hpp"

namespace zmbt {

namespace detail {

template <class T>
inline auto static_ptr_cast(std::shared_ptr<void> obj)
{
    return std::static_pointer_cast<T>(obj);
};

template <>
inline auto static_ptr_cast<nullptr_t>(std::shared_ptr<void> obj)
{
    return nullptr;
};

}



class TriggerObj
{
    object_id id_;
    bool is_unsafe_ptr_;
    std::shared_ptr<void> ptr_;

    static void stub_delete(void*) {}

public:

    template <class T>
    TriggerObj(std::shared_ptr<T> obj)
        : id_{(remove_cvref_t<T>*)(obj.get())}
        , is_unsafe_ptr_{false}
        , ptr_{obj}
    {
    }

    template <class T>
    TriggerObj(T* obj)
        : id_{obj}
        , is_unsafe_ptr_{true}
        , ptr_{std::shared_ptr<void>(obj, stub_delete)}
    {
    }

    template <class T>
    TriggerObj(T const* obj) : TriggerObj(const_cast<T*>(obj))
    {
    }

    template <class T>
    TriggerObj(T& obj) : TriggerObj(&obj)
    {
    }

    template <class T>
    TriggerObj(T const& obj) : TriggerObj(&obj)
    {
    }


    TriggerObj(nullptr_t)
        : id_{nullptr}
        , is_unsafe_ptr_{false}
        , ptr_{nullptr}
    {
    }

    virtual ~TriggerObj() = default;

    TriggerObj(TriggerObj const&) = default;
    TriggerObj(TriggerObj &&) = default;
    TriggerObj& operator=(TriggerObj const&) = default;
    TriggerObj& operator=(TriggerObj &&) = default;

    object_id id() const
    {
        return id_;
    }

    bool unsafe() const
    {
        return is_unsafe_ptr_;
    }

    std::shared_ptr<void> ptr() const
    {
        return ptr_;
    }
};



class TriggerIfc
{
    interface_id id_;
    std::function<boost::json::value(std::shared_ptr<void>, boost::json::value const&)> fn_;

    template <class R>
    static auto apply_fn(std::function<R()> fn) -> mp_if<is_void<R>, boost::json::value>
    {
        fn();
        return nullptr;
    }

    template <class R>
    static auto apply_fn(std::function<R()> fn) -> mp_if<mp_not<is_void<R>>, boost::json::value>
    {
        auto ret = fn();
        return json_from(ret);
    }

public:

    template <class I>
    TriggerIfc(I&& interface)
        : id_{std::forward<I>(interface)}
        , fn_{[ifc_ptr = get_ifc_pointer(std::forward<I>(interface))]
            (std::shared_ptr<void> obj, boost::json::value const& args_in) -> boost::json::value {
            using reflection = reflect::invocation<I const&>;
            using return_t = typename reflection::return_t;
            using args_t = typename reflection::args_t;
            using args_unqf_t = tuple_unqf_t<args_t>;
            using ifc_host_unref_t = remove_reference_t<typename reflection::host_t>;

            args_unqf_t stored_args = args_in.is_array()
                ? dejsonize<args_unqf_t>(args_in)
                : dejsonize<args_unqf_t>(boost::json::array{args_in});

            args_t test_args = convert_tuple_to<args_t>(stored_args);

            auto ret = apply_fn<return_t>([obj, ifc_ptr, test_args]() {
                if (is_member_function_pointer<I>::value && !obj) {
                    throw environment_error("invoking mfp trigger with null object");
                }
                // WARN: is_unsafe_ptr cast
                // TODO: check type_index by comp option
                return reflection::apply(detail::static_ptr_cast<ifc_host_unref_t>(obj), ifc_ptr, test_args);
            });

            return {
                {"args", json_from(convert_tuple_to<args_unqf_t>(test_args))},
                {"return", ret},
                {"ts", get_ts()},
                {"tid", get_tid()},
            };
        }}
    {
    }

    virtual ~TriggerIfc() = default;

    TriggerIfc(TriggerIfc const&) = default;
    TriggerIfc(TriggerIfc &) = default;
    TriggerIfc(TriggerIfc &&) = default;
    TriggerIfc& operator=(TriggerIfc const&) = default;
    TriggerIfc& operator=(TriggerIfc &&) = default;

    interface_id id() const
    {
        return id_;
    }

    boost::json::value operator()(std::shared_ptr<void> obj, boost::json::value const& args) const
    {
        return fn_(obj, args);
    }
};

class Trigger final {
    struct internal_ctor {};
    TriggerObj obj_;
    TriggerIfc ifc_;

    Trigger(internal_ctor, TriggerObj const& obj, TriggerIfc const& ifc) : obj_{obj}, ifc_{ifc}
    {
    }


  public:

    ~Trigger() = default;
    Trigger(Trigger const&) = default;
    Trigger(Trigger &&) = default;
    Trigger& operator=(Trigger const&) = default;
    Trigger& operator=(Trigger &&) = default;

    template <class T, class I>
    Trigger(T&& obj, I&& interface)
        : Trigger(internal_ctor{}, TriggerObj(std::forward<T>(obj)), TriggerIfc(std::forward<I>(interface)))
    {
    }

    template <class T, class I, class... A>
    Trigger(type_tag<T>, I&& interface, A&&... args)
        : Trigger(std::make_shared<T>(std::forward<A>(args)...), std::forward<I>(interface))
    {
    }


    boost::json::value execute(boost::json::value const& args_in = boost::json::array{}) const
    {
        return ifc_(obj_.ptr(), args_in);
    }

    boost::json::value operator()(boost::json::value const& args_in = boost::json::array{}) const
    {
        return execute(args_in);
    }

    bool operator==(Trigger const& o) const
    {
        return (obj_.id() == o.obj_.id()) && (ifc_.id() == o.ifc_.id());
    }

    bool operator!=(Trigger const& o) const
    {
        return !this->operator==(o);
    }

    object_id obj_id() const
    {
        return obj_.id();
    }

    interface_id ifc_id() const
    {
        return ifc_.id();
    }

};


}  // namespace zmbt

#endif