Files
meta.hpp/headers/meta.hpp/meta_method_info.hpp
2021-07-05 00:10:46 +07:00

203 lines
7.4 KiB
C++

/*******************************************************************************
* This file is part of the "https://github.com/blackmatov/meta.hpp"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2021, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "meta_fwd.hpp"
#include "meta_value.hpp"
#include "meta_data_info.hpp"
namespace meta_hpp::method_detail
{
template < typename Method >
struct method_traits;
template < typename R, typename Base, typename... Args >
struct method_traits<R(Base::*)(Args...)> {
static constexpr bool is_const = false;
static constexpr std::size_t arity = sizeof...(Args);
using return_type = R;
using instance_type = Base;
using argument_types = std::tuple<Args...>;
};
template < typename R, typename Base, typename... Args >
struct method_traits<R(Base::*)(Args...) const>
: method_traits<R(Base::*)(Args...)> {
static constexpr bool is_const = true;
};
template < typename R, typename Base, typename... Args >
struct method_traits<R(Base::*)(Args...) noexcept>
: method_traits<R(Base::*)(Args...)> {};
template < typename R, typename Base, typename... Args >
struct method_traits<R(Base::*)(Args...) const noexcept>
: method_traits<R(Base::*)(Args...) const> {};
template < auto Method, std::size_t... Is >
std::optional<value> invoke([[maybe_unused]] void* instance, value* args, std::index_sequence<Is...>) {
using mt = method_traits<decltype(Method)>;
using return_type = typename mt::return_type;
using instance_type = typename mt::instance_type;
using argument_types = typename mt::argument_types;
[[maybe_unused]] auto typed_arguments = std::make_tuple(
(args + Is)->try_cast<std::tuple_element_t<Is, argument_types>>()...);
if ( !(std::get<Is>(typed_arguments) && ...) ) {
throw std::logic_error("an attempt to call a method with incorrect argument types");
}
if constexpr ( std::is_void_v<return_type> ) {
std::invoke(Method,
std::ref(*static_cast<instance_type*>(instance)),
*std::get<Is>(typed_arguments)...);
return std::nullopt;
} else {
return_type return_value = std::invoke(Method,
std::ref(*static_cast<instance_type*>(instance)),
*std::get<Is>(typed_arguments)...);
return value{std::move(return_value)};
}
}
template < auto Method >
std::optional<value> invoke(void* instance, value* args, std::size_t arg_count) {
using mt = method_traits<decltype(Method)>;
if ( arg_count != mt::arity ) {
throw std::logic_error("an attempt to call a method with an incorrect arity");
}
return invoke<Method>(instance, args, std::make_index_sequence<mt::arity>());
}
template < auto Method, std::size_t... Is >
std::optional<value> cinvoke([[maybe_unused]] const void* instance, value* args, std::index_sequence<Is...>) {
using mt = method_traits<decltype(Method)>;
using return_type = typename mt::return_type;
using instance_type = typename mt::instance_type;
using argument_types = typename mt::argument_types;
[[maybe_unused]] auto typed_arguments = std::make_tuple(
(args + Is)->try_cast<std::tuple_element_t<Is, argument_types>>()...);
if ( !(std::get<Is>(typed_arguments) && ...) ) {
throw std::logic_error("an attempt to call a method with incorrect argument types");
}
if constexpr ( mt::is_const ) {
if constexpr ( std::is_void_v<return_type> ) {
std::invoke(Method,
std::ref(*static_cast<const instance_type*>(instance)),
*std::get<Is>(typed_arguments)...);
return std::nullopt;
} else {
return_type return_value = std::invoke(Method,
std::ref(*static_cast<const instance_type*>(instance)),
*std::get<Is>(typed_arguments)...);
return value{std::move(return_value)};
}
} else {
throw std::logic_error("an attempt to call a non-constant method by constant instance");
}
}
template < auto Method >
std::optional<value> cinvoke(const void* instance, value* args, std::size_t arg_count) {
using mt = method_traits<decltype(Method)>;
if ( arg_count != mt::arity ) {
throw std::logic_error("an attempt to call a method with a different arity");
}
return cinvoke<Method>(instance, args, std::make_index_sequence<mt::arity>());
}
}
namespace meta_hpp
{
class method_info {
public:
method_info() = delete;
method_info(method_info&&) = default;
method_info(const method_info&) = default;
method_info& operator=(method_info&&) = default;
method_info& operator=(const method_info&) = default;
public:
const family_id& fid() const noexcept {
return fid_;
}
const std::string& id() const noexcept {
return id_;
}
template < typename... Args >
std::optional<value> invoke(void* instance, Args&&... args) const {
if constexpr ( sizeof...(Args) > 0u ) {
std::array<value, sizeof...(Args)> vargs{std::forward<Args>(args)...};
return invoke_(instance, vargs.data(), vargs.size());
} else {
return invoke_(instance, nullptr, 0u);
}
}
template < typename... Args >
std::optional<value> invoke(const void* instance, Args&&... args) const {
if constexpr ( sizeof...(Args) > 0u ) {
std::array<value, sizeof...(Args)> vargs{std::forward<Args>(args)...};
return cinvoke_(instance, vargs.data(), vargs.size());
} else {
return cinvoke_(instance, nullptr, 0u);
}
}
template < typename F >
void each_data(F&& f) const {
for ( auto&& id_info : datas_ ) {
std::invoke(f, id_info.second);
}
}
template < typename F >
void visit(F&& f) const {
each_data(f);
}
std::optional<data_info> get_data(std::string_view id) const {
return detail::find_opt(datas_, id);
}
void merge(const method_info& other) {
if ( fid() != other.fid() ) {
throw std::logic_error("method_info::merge failed");
}
detail::merge_with(datas_, other.datas_, &data_info::merge);
}
private:
template < auto Method >
friend class method_;
template < typename MethodType, MethodType Method >
method_info(detail::auto_arg_t<Method>, std::string id)
: fid_{get_family_id<MethodType>()}
, id_{std::move(id)}
, invoke_{&method_detail::invoke<Method>}
, cinvoke_{&method_detail::cinvoke<Method>} {}
private:
family_id fid_;
std::string id_;
std::optional<value>(*invoke_)(void*, value*, std::size_t);
std::optional<value>(*cinvoke_)(const void*, value*, std::size_t);
std::map<std::string, data_info, std::less<>> datas_;
};
}