/******************************************************************************* * 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-2024, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ #pragma once #include "../meta_base.hpp" #include "../meta_states.hpp" #include "../meta_detail/type_registry.hpp" #include "../meta_detail/value_utilities/uarg.hpp" #include "../meta_types/constructor_type.hpp" namespace meta_hpp::detail { template < constructor_policy::family Policy, class_kind Class, typename... Args > uvalue raw_constructor_create(type_registry& registry, std::span args) { using ct = constructor_traits; using class_type = typename ct::class_type; using argument_types = typename ct::argument_types; constexpr bool as_object // = std::is_same_v; constexpr bool as_raw_ptr // = std::is_same_v; constexpr bool as_shared_ptr // = std::is_same_v; constexpr bool as_unique_ptr // = std::is_same_v; static_assert(as_object || as_raw_ptr || as_shared_ptr || as_unique_ptr); META_HPP_ASSERT( // args.size() == ct::arity // && "an attempt to call a constructor with an incorrect arity" ); META_HPP_ASSERT( // can_cast_all_uargs(registry, args) // && "an attempt to call a constructor with incorrect argument types" ); return unchecked_call_with_uargs(registry, args, [](auto&&... all_args) -> uvalue { if constexpr ( as_object ) { return make_uvalue(META_HPP_FWD(all_args)...); } if constexpr ( as_raw_ptr ) { return std::make_unique(META_HPP_FWD(all_args)...).release(); } if constexpr ( as_shared_ptr ) { return std::make_shared(META_HPP_FWD(all_args)...); } if constexpr ( as_unique_ptr ) { return std::make_unique(META_HPP_FWD(all_args)...); } }); } template < class_kind Class, typename... Args > uvalue raw_constructor_create_at(type_registry& registry, void* mem, std::span args) { using ct = constructor_traits; using class_type = typename ct::class_type; using argument_types = typename ct::argument_types; META_HPP_ASSERT( // args.size() == ct::arity // && "an attempt to call a constructor with an incorrect arity" ); META_HPP_ASSERT( // can_cast_all_uargs(registry, args) // && "an attempt to call a constructor with incorrect argument types" ); return unchecked_call_with_uargs(registry, args, [mem](auto&&... all_args) { return std::construct_at(static_cast(mem), META_HPP_FWD(all_args)...); }); } template < class_kind Class, typename... Args > uerror raw_constructor_create_error(type_registry& registry, std::span args) noexcept { using ct = constructor_traits; using argument_types = typename ct::argument_types; if ( args.size() != ct::arity ) { return uerror{error_code::arity_mismatch}; } if ( !can_cast_all_uargs(registry, args) ) { return uerror{error_code::argument_type_mismatch}; } return uerror{error_code::no_error}; } } namespace meta_hpp::detail { template < constructor_policy::family Policy, class_kind Class, typename... Args > constructor_state::create_impl make_constructor_create(type_registry& registry) { return [®istry](std::span args) { // return raw_constructor_create(registry, args); }; } template < class_kind Class, typename... Args > constructor_state::create_at_impl make_constructor_create_at(type_registry& registry) { return [®istry](void* mem, std::span args) { // return raw_constructor_create_at(registry, mem, args); }; } template < class_kind Class, typename... Args > constructor_state::create_error_impl make_constructor_create_error(type_registry& registry) { return [®istry](std::span args) { // return raw_constructor_create_error(registry, args); }; } template < class_kind Class, typename... Args > argument_list make_constructor_arguments() { using ct = constructor_traits; using ct_argument_types = typename ct::argument_types; return [](std::index_sequence) { [[maybe_unused]] const auto make_argument = [](index_constant) { using P = type_list_at_t; return argument{argument_state::make

(I, metadata_map{})}; }; return argument_list{make_argument(index_constant{})...}; }(std::make_index_sequence()); } } namespace meta_hpp::detail { inline constructor_state::constructor_state(constructor_index nindex, metadata_map nmetadata) : index{nindex} , metadata{std::move(nmetadata)} {} template < constructor_policy::family Policy, class_kind Class, typename... Args > constructor_state_ptr constructor_state::make(metadata_map metadata) { type_registry& registry{type_registry::instance()}; constructor_state state{ constructor_index{registry.resolve_constructor_type()}, std::move(metadata), }; state.create = make_constructor_create(registry); state.create_at = make_constructor_create_at(registry); state.create_error = make_constructor_create_error(registry); state.arguments = make_constructor_arguments(); return std::make_shared(std::move(state)); } } namespace meta_hpp { inline constructor_type constructor::get_type() const noexcept { return state_->index.get_type(); } template < typename... Args > uvalue constructor::create(Args&&... args) const { using namespace detail; type_registry& registry{type_registry::instance()}; const std::array vargs{uarg{registry, META_HPP_FWD(args)}...}; return state_->create(vargs); } template < typename... Args > uresult constructor::try_create(Args&&... args) const { using namespace detail; type_registry& registry{type_registry::instance()}; { // doesn't actually move 'args', just checks conversion errors const std::array vargs{uarg_base{registry, META_HPP_FWD(args)}...}; if ( const uerror err = state_->create_error(vargs) ) { return err; } } const std::array vargs{uarg{registry, META_HPP_FWD(args)}...}; return state_->create(vargs); } template < typename... Args > uvalue constructor::create_at(void* mem, Args&&... args) const { using namespace detail; type_registry& registry{type_registry::instance()}; const std::array vargs{uarg{registry, META_HPP_FWD(args)}...}; return state_->create_at(mem, vargs); } template < typename... Args > uresult constructor::try_create_at(void* mem, Args&&... args) const { using namespace detail; type_registry& registry{type_registry::instance()}; { // doesn't actually move 'args', just checks conversion errors const std::array vargs{uarg_base{registry, META_HPP_FWD(args)}...}; if ( const uerror err = state_->create_error(vargs) ) { return err; } } const std::array vargs{uarg{registry, META_HPP_FWD(args)}...}; return state_->create_at(mem, vargs); } template < typename... Args > bool constructor::is_invocable_with() const noexcept { return !check_invocable_error(); } template < typename... Args > bool constructor::is_invocable_with(Args&&... args) const noexcept { return !check_invocable_error(META_HPP_FWD(args)...); } template < typename... Args > uerror constructor::check_invocable_error() const noexcept { using namespace detail; type_registry& registry{type_registry::instance()}; const std::array vargs{uarg_base{registry, type_list{}}...}; return state_->create_error(vargs); } template < typename... Args > uerror constructor::check_invocable_error(Args&&... args) const noexcept { using namespace detail; type_registry& registry{type_registry::instance()}; const std::array vargs{uarg_base{registry, META_HPP_FWD(args)}...}; return state_->create_error(vargs); } inline argument constructor::get_argument(std::size_t position) const noexcept { return position < state_->arguments.size() ? state_->arguments[position] : argument{}; } inline const argument_list& constructor::get_arguments() const noexcept { return state_->arguments; } }