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 "zmbt/expr/expression.hpp"
namespace zmbt {
class Environment::InterfaceHandle
{
private:
object_id refobj_;
interface_id interface_;
protected:
mutable zmbt::Environment env;
std::shared_ptr<OutputRecorder> output_recorder_;
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 PrototypeReturn() const;
boost::json::array PrototypeArgs() const;
void EnableOutputRecordFor(ChannelKind const kind);
void MaybeThrowException();
boost::json::value YieldInjection(ChannelKind const kind);
boost::json::value YieldInjectionArgs()
{
return YieldInjection(ChannelKind::Args);
}
boost::json::value YieldInjectionReturn()
{
return YieldInjection(ChannelKind::Return);
}
void Inject(std::shared_ptr<Generator> gen, lang::Expression const& tf, ChannelKind const kind, boost::json::string_view jp = "");
void InjectReturn(lang::Expression const& e, boost::json::string_view jp = "")
{
return Inject(std::make_shared<Generator>(e), expr::Noop, ChannelKind::Return, jp);
}
void InjectArgs(lang::Expression const& e, boost::json::string_view jp = "")
{
return Inject(std::make_shared<Generator>(e), expr::Noop, ChannelKind::Args, jp);
}
std::size_t ObservedCalls() const;
boost::json::array CaptureSlice(boost::json::string_view signal_path) const;
boost::json::array const& Captures() const;
boost::json::string const& key() const;
InterfaceHandle& RunAsAction();
InterfaceHandle& RunAsTrigger(std::size_t const repeats = 1);
};
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>;
using return_or_nullptr_t = reflect::invocation_ret_unqf_or_nullptr_t<Interface const&>;
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>;
void HookArgsImpl(hookout_args_t & args)
try
{
output_recorder_->push(convert_tuple_to<unqf_args_t>(args), ErrorOr<return_or_nullptr_t>());
auto const injection = YieldInjection(ChannelKind::Args).as_array();
if (injection.size() != std::tuple_size<unqf_args_t>())
{
env.SetTestError({
{"error" , "invalid inject arguments arity"},
{"injection", injection},
{"interface", interface().str() },
{"context" , "Hook" },
{"injection", injection },
});
}
else
{
auto args_out = dejsonize<unqf_args_t>(injection);
tuple_exchange(args, args_out);
}
}
catch(const std::exception& e)
{
env.SetTestError({
{"error" , "exception thrown at args evaluation"},
{"interface", interface().str() },
{"context" , "Hook" },
{"what" , e.what() },
});
}
void HookReturnImpl(type_tag<void>)
{
}
template <class T>
auto HookReturnImpl(type_tag<T>) -> mp_if<mp_not<is_reference<T>>, T>
try
{
boost::json::value result(YieldInjection(ChannelKind::Return));
return dejsonize<T>(result);
}
catch(const std::exception& e)
{
env.SetTestError({
{"error" , "exception thrown at return evaluation"},
{"interface", interface().str() },
{"context" , "Hook" },
{"what" , e.what() },
});
return dejsonize<T>(PrototypeReturn());
}
template <class T>
auto HookReturnImpl(type_tag<T>) -> mp_if<is_reference<T>, T>
{
using TT = remove_cvref_t<T>;
TT value = HookReturnImpl(type<TT>);
auto const key = format("$(ret-ref-%s-%s)", interface().key(), refobj().key());
TT& ref = Env().template GetSharedRef<TT>(key, reflect::signal_traits<TT>::init());
ref = value;
return ref;
}
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;
HookArgsImpl(args);
MaybeThrowException();
return HookReturnImpl(type<return_t>);
}
template <class... A>
return_t Hook(A&&... arg)
{
hookout_args_t args {arg...};
return Hook(args);
}
};
template <class Interface>
Environment::TypedInterfaceHandle<Interface> InterfaceRecord(Interface const& interface, object_id const& obj = {ifc_host_nullptr<Interface>})
{
Environment env {};
env.InitializeInterfaceHandlers(interface);
return {interface, obj};
}
} // namespace zmbt
#endif // ZMBT_MAPPING_ENVIRONMENT_INTERFACE_RECORD_HPP_