File environment_interface_record.hpp¶
File List > backends > cxx > include > zmbt > model > environment_interface_record.hpp
Go to the documentation of this file
#ifndef ZMBT_MODEL_ENVIRONMENT_INTERFACE_RECORD_HPP_
#define ZMBT_MODEL_ENVIRONMENT_INTERFACE_RECORD_HPP_
#include <chrono>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include "environment.hpp"
#include "expression.hpp"
namespace zmbt {
class Environment::InterfaceHandle
{
mutable zmbt::Environment env;
object_id refobj_;
interface_id interface_;
protected:
JsonNode captures;
JsonNode injects;
public:
interface_id interface() const
{
return interface_;
}
object_id refobj() const
{
return refobj_;
}
InterfaceHandle(Environment const& e, interface_id const& interface, object_id refobj);
InterfaceHandle(Environment const& e, boost::json::string_view ref);
public:
InterfaceHandle(interface_id const& interface, object_id refobj);
InterfaceHandle(interface_id const& interface, nullptr_t);
InterfaceHandle(boost::json::string_view ref);
template<class H, class E = mp_if<mp_not<is_pointer<H>>, void>>
InterfaceHandle(interface_id const& interface, H const& obj)
: InterfaceHandle(interface, std::addressof(obj))
{
}
InterfaceHandle(InterfaceHandle const&) = default;
InterfaceHandle(InterfaceHandle && o) = default;
InterfaceHandle& operator=(InterfaceHandle const&) = default;
InterfaceHandle& operator=(InterfaceHandle &&) = default;
~InterfaceHandle() = default;
Environment& Env() const
{
return env;
}
boost::json::value const& PrototypeReturn() const;
boost::json::value const& PrototypeArgs() const;
boost::json::value GetInjection(boost::json::string_view group, boost::json::string_view jp, int const nofcall);
boost::json::value GetInjectionReturn(boost::json::string_view jp, std::size_t const nofcall = 0)
{
return GetInjection("return", jp, nofcall);
}
boost::json::value GetInjectionReturn(std::size_t const nofcall = 0)
{
return GetInjectionReturn("", nofcall);
}
boost::json::value GetInjectionArgs(boost::json::string_view jp, std::size_t const nofcall = 0)
{
return GetInjection("args", jp, nofcall);
}
boost::json::value GetInjectionArgs(std::size_t const nofcall = 0)
{
return GetInjectionArgs("", nofcall);
}
void Inject(Expression const& e, boost::json::string_view op, boost::json::string_view group, boost::json::string_view jp = "");
void InjectReturn(Expression const& e, SignalOperatorHandler const& op, boost::json::string_view jp = "")
{
return Inject(e, op.annotation(), "return", jp);
}
void InjectReturn(Expression const& e, boost::json::string_view jp = "")
{
return InjectReturn(e, {}, jp);
}
void InjectArgs(Expression const& e, SignalOperatorHandler const& op, boost::json::string_view jp = "")
{
return Inject(e, op.annotation(), "args", jp);
}
void InjectArgs(Expression const& e, boost::json::string_view jp = "")
{
return InjectArgs(e, {}, jp);
}
std::size_t ObservedCalls() const;
boost::json::array ObservedArgs(int const nofcall = -1);
boost::json::array CaptureSlice(boost::json::string_view signal_path, int start = 0, int stop = -1, int const step = 1);
boost::json::array const& Captures();
boost::json::value ObservedReturn(int const nofcall = -1);
boost::json::string const& key() const;
InterfaceHandle& RunAsAction();
InterfaceHandle& RunAsTrigger(std::size_t const nofcall = 0);
};
template <class Interface>
class Environment::TypedInterfaceHandle : public Environment::InterfaceHandle
{
using reflection = reflect::invocation<Interface const&>;
using return_t = typename reflection::return_t;
using args_t = typename reflection::args_t;
using unqf_args_t = tuple_unqf_t<args_t>;
template <class T>
using rvalue_reference_to_value = mp_if<std::is_rvalue_reference<T>, std::remove_reference_t<T>, T>;
using hookout_args_t = mp_transform<rvalue_reference_to_value, args_t>;
std::size_t HookImpl(hookout_args_t & args)
{
auto const ts = get_ts();
std::string const tid = get_tid();
auto lock = Env().Lock();
captures("/+") = {
{"ts", ts},
{"tid", tid },
{"args", json_from(convert_tuple_to<unqf_args_t>(args))}
};
auto nofcall = captures().as_array().size() - 1;
auto const injection = GetInjectionArgs(nofcall).as_array();
if (injection.size() != std::tuple_size<unqf_args_t>())
{
throw model_error("invalid inject arguments arity");
}
auto args_out = dejsonize<unqf_args_t>(injection);
tuple_exchange(args, args_out);
// Produce
return nofcall;
}
public:
template <class H>
TypedInterfaceHandle(interface_id const& interface, H const& refobj)
: Environment::InterfaceHandle(interface, refobj)
{
}
TypedInterfaceHandle(TypedInterfaceHandle const&) = default;
TypedInterfaceHandle(TypedInterfaceHandle &&) = default;
~TypedInterfaceHandle() = default;
return_t Hook(hookout_args_t args)
{
std::size_t nofcall;
boost::json::value result;
try
{
nofcall = HookImpl(args);
}
catch(const std::exception& e)
{
throw model_error("Hook #%d %s capture error: `%s`, args: %s"
, nofcall, interface()
, e.what(), json_from(args));
return dejsonize<return_t>(nullptr);
}
try
{
result = GetInjectionReturn(nofcall);
}
catch(const std::exception& e)
{
throw model_error("Hook #%d %s return evaluation error: `%s`", nofcall, interface(), e.what());
return dejsonize<return_t>(nullptr);
}
try
{
return dejsonize<return_t>(result);
}
catch(const std::exception& e)
{
throw model_error("Hook #%d %s return evaluation error, can't deserialize %s as %s",
nofcall, interface(), boost::json::serialize(result), type_name<return_t>());
return dejsonize<return_t>(nullptr);
}
}
template <class... A>
return_t Hook(A&&... arg)
{
hookout_args_t args {arg...};
return Hook(args);
}
};
template <class I>
Environment::TypedInterfaceHandle<I> InterfaceRecord(I const& interface, object_id const& obj = {ifc_host_nullptr<I>})
{
Environment env {};
env.RegisterPrototypes(interface);
return {interface, obj};
}
} // namespace zmbt
#endif // ZMBT_MAPPING_ENVIRONMENT_INTERFACE_RECORD_HPP_