invoke functions, methods and ctors with static arguments

This commit is contained in:
BlackMATov
2021-08-13 13:10:33 +07:00
parent 4d400af7a0
commit 442c80ffb9
13 changed files with 1739 additions and 17 deletions

View File

@@ -88,6 +88,7 @@ namespace meta_hpp
namespace meta_hpp
{
class arg;
class inst;
class value;
}

View File

@@ -20,6 +20,12 @@ namespace meta_hpp
explicit operator bool() const noexcept;
const ctor_type& type() const noexcept;
template < typename... Args >
value invoke(Args&&... args) const;
template < typename... Args >
bool is_invocable_with() const noexcept;
public:
template < typename F >
void visit(F&& f) const;
@@ -39,11 +45,94 @@ namespace meta_hpp
};
}
namespace meta_hpp::detail
{
using ctor_invoke = std::function<value(const arg*, std::size_t)>;
template < typename Class, typename... Args, std::size_t... Is >
value raw_ctor_invoke_impl(
const arg* args,
std::index_sequence<Is...>)
{
using ct = ctor_traits<Class, Args...>;
using class_type = typename ct::class_type;
using argument_types = typename ct::argument_types;
if ( !(... && (args + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>()) ) {
throw std::logic_error("an attempt to call a ctor with an incorrect argument types");
}
class_type class_value{(args + Is)->cast<std::tuple_element_t<Is, argument_types>>()...};
return value{std::forward<class_type>(class_value)};
}
template < typename Class, typename... Args >
value raw_ctor_invoke(
const arg* args,
std::size_t arg_count)
{
using ct = ctor_traits<Class, Args...>;
if ( arg_count != ct::arity ) {
throw std::logic_error("an attempt to call a ctor with an incorrect arity");
}
return raw_ctor_invoke_impl<Class, Args...>(
args,
std::make_index_sequence<ct::arity>());
}
template < typename Class, typename... Args >
ctor_invoke make_ctor_invoke() {
using namespace std::placeholders;
return std::bind(&raw_ctor_invoke<Class, Args...>, _1, _2);
}
}
namespace meta_hpp::detail
{
using ctor_is_invocable_with = std::function<bool(const arg_base*, std::size_t)>;
template < typename Class, typename... Args, std::size_t... Is >
bool raw_ctor_is_invocable_with_impl(
const arg_base* arg_bases,
std::index_sequence<Is...>)
{
using ct = ctor_traits<Class, Args...>;
using argument_types = typename ct::argument_types;
return (... && (arg_bases + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>() );
}
template < typename Class, typename... Args >
bool raw_ctor_is_invocable_with(
const arg_base* arg_bases,
std::size_t arg_count)
{
using ct = ctor_traits<Class, Args...>;
if ( arg_count != ct::arity ) {
return false;
}
return raw_ctor_is_invocable_with_impl<Class, Args...>(
arg_bases,
std::make_index_sequence<ct::arity>());
}
template < typename Class, typename... Args >
ctor_is_invocable_with make_ctor_is_invocable_with() {
using namespace std::placeholders;
return std::bind(&raw_ctor_is_invocable_with<Class, Args...>, _1, _2);
}
}
namespace meta_hpp
{
struct ctor_info::state final {
ctor_type type;
data_info_map datas;
detail::ctor_invoke invoke;
detail::ctor_is_invocable_with is_invocable_with;
};
}
@@ -61,6 +150,18 @@ namespace meta_hpp
inline const ctor_type& ctor_info::type() const noexcept {
return state_->type;
}
template < typename... Args >
value ctor_info::invoke(Args&&... args) const {
std::array<arg, sizeof...(Args)> vargs{arg{std::forward<Args>(args)}...};
return state_->invoke(vargs.data(), vargs.size());
}
template < typename... Args >
bool ctor_info::is_invocable_with() const noexcept {
std::array<arg_base, sizeof...(Args)> arg_bases{arg_base{typename_arg<Args>}...};
return state_->is_invocable_with(arg_bases.data(), arg_bases.size());
}
}
namespace meta_hpp
@@ -88,6 +189,8 @@ namespace meta_hpp
ctor_info::ctor_info(typename_arg_t<Class>, typename_arg_t<Args...>)
: state_{std::make_shared<state>(state{
ctor_type{typename_arg<Class>, typename_arg<Args...>},
{}
{},
detail::make_ctor_invoke<Class, Args...>(),
detail::make_ctor_is_invocable_with<Class, Args...>(),
})} {}
}

View File

@@ -21,6 +21,12 @@ namespace meta_hpp
const std::string& name() const noexcept;
const function_type& type() const noexcept;
template < typename... Args >
std::optional<value> invoke(Args&&... args) const;
template < typename... Args >
bool is_invocable_with() const noexcept;
public:
template < typename F >
void visit(F&& f) const;
@@ -33,19 +39,112 @@ namespace meta_hpp
template < typename Function > friend class function_;
template < typename Function >
explicit function_info(std::string name, Function instance);
explicit function_info(std::string name, Function function);
private:
struct state;
std::shared_ptr<state> state_;
};
}
namespace meta_hpp::detail
{
using function_invoke = std::function<std::optional<value>(const arg*, std::size_t)>;
template < typename Function, std::size_t... Is >
std::optional<value> raw_function_invoke_impl(
Function function,
const arg* args,
std::index_sequence<Is...>)
{
using ft = function_pointer_traits<Function>;
using return_type = typename ft::return_type;
using argument_types = typename ft::argument_types;
if ( !(... && (args + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>()) ) {
throw std::logic_error("an attempt to call a function with an incorrect argument types");
}
if constexpr ( std::is_void_v<return_type> ) {
std::invoke(function,
(args + Is)->cast<std::tuple_element_t<Is, argument_types>>()...);
return std::nullopt;
} else {
return_type return_value{std::invoke(function,
(args + Is)->cast<std::tuple_element_t<Is, argument_types>>()...)};
return value{std::forward<return_type>(return_value)};
}
}
template < typename Function >
std::optional<value> raw_function_invoke(
Function function,
const arg* args,
std::size_t arg_count)
{
using ft = function_pointer_traits<Function>;
if ( arg_count != ft::arity ) {
throw std::logic_error("an attempt to call a function with an incorrect arity");
}
return raw_function_invoke_impl<Function>(
function,
args,
std::make_index_sequence<ft::arity>());
}
template < typename Function >
function_invoke make_function_invoke(Function function) {
using namespace std::placeholders;
return std::bind(&raw_function_invoke<Function>, function, _1, _2);
}
}
namespace meta_hpp::detail
{
using function_is_invocable_with = std::function<bool(const arg_base*, std::size_t)>;
template < typename Function, std::size_t... Is >
bool raw_function_is_invocable_with_impl(
const arg_base* arg_bases,
std::index_sequence<Is...>)
{
using ft = function_pointer_traits<Function>;
using argument_types = typename ft::argument_types;
return (... && (arg_bases + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>() );
}
template < typename Function >
bool raw_function_is_invocable_with(
const arg_base* arg_bases,
std::size_t arg_count)
{
using ft = function_pointer_traits<Function>;
if ( arg_count != ft::arity ) {
return false;
}
return raw_function_is_invocable_with_impl<Function>(
arg_bases,
std::make_index_sequence<ft::arity>());
}
template < typename Function >
function_is_invocable_with make_function_is_invocable_with() {
using namespace std::placeholders;
return std::bind(&raw_function_is_invocable_with<Function>, _1, _2);
}
}
namespace meta_hpp
{
struct function_info::state final {
std::string name;
function_type type;
data_info_map datas;
detail::function_invoke invoke;
detail::function_is_invocable_with is_invocable_with;
};
}
@@ -67,6 +166,18 @@ namespace meta_hpp
inline const function_type& function_info::type() const noexcept {
return state_->type;
}
template < typename... Args >
std::optional<value> function_info::invoke(Args&&... args) const {
std::array<arg, sizeof...(Args)> vargs{arg{std::forward<Args>(args)}...};
return state_->invoke(vargs.data(), vargs.size());
}
template < typename... Args >
bool function_info::is_invocable_with() const noexcept {
std::array<arg_base, sizeof...(Args)> arg_bases{arg_base{typename_arg<Args>}...};
return state_->is_invocable_with(arg_bases.data(), arg_bases.size());
}
}
namespace meta_hpp
@@ -91,12 +202,12 @@ namespace meta_hpp
namespace meta_hpp
{
template < typename Function >
function_info::function_info(std::string name, Function instance)
function_info::function_info(std::string name, Function function)
: state_{std::make_shared<state>(state{
std::move(name),
type_db::get<Function>().template as<function_type>(),
{}
})} {
(void)instance;
}
{},
detail::make_function_invoke<Function>(function),
detail::make_function_is_invocable_with<Function>(),
})} {}
}

View File

@@ -21,6 +21,12 @@ namespace meta_hpp
const std::string& name() const noexcept;
const method_type& type() const noexcept;
template < typename Instance, typename... Args >
std::optional<value> invoke(Instance&& instance, Args&&... args) const;
template < typename Inst, typename... Args >
bool is_invocable_with() const noexcept;
public:
template < typename F >
void visit(F&& f) const;
@@ -33,19 +39,128 @@ namespace meta_hpp
template < typename Method > friend class method_;
template < typename Method >
explicit method_info(std::string name, Method instance);
explicit method_info(std::string name, Method method);
private:
struct state;
std::shared_ptr<state> state_;
};
}
namespace meta_hpp::detail
{
using method_invoke = std::function<std::optional<value>(const inst&, const arg*, std::size_t)>;
template < typename Method, std::size_t... Is >
std::optional<value> raw_method_invoke_impl(
Method method,
const inst& inst,
const arg* args,
std::index_sequence<Is...>)
{
using mt = method_pointer_traits<Method>;
using return_type = typename mt::return_type;
using qualified_type = typename mt::qualified_type;
using argument_types = typename mt::argument_types;
if ( !inst.can_cast_to<qualified_type>() ) {
throw std::logic_error("an attempt to call a method with an incorrect instance type");
}
if ( !(... && (args + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>()) ) {
throw std::logic_error("an attempt to call a method with an incorrect argument types");
}
if constexpr ( std::is_void_v<return_type> ) {
std::invoke(method,
inst.cast<qualified_type>(),
(args + Is)->cast<std::tuple_element_t<Is, argument_types>>()...);
return std::nullopt;
} else {
return_type return_value{std::invoke(method,
inst.cast<qualified_type>(),
(args + Is)->cast<std::tuple_element_t<Is, argument_types>>()...)};
return value{std::forward<return_type>(return_value)};
}
}
template < typename Method >
std::optional<value> raw_method_invoke(
Method method,
const inst& inst,
const arg* args,
std::size_t arg_count)
{
using mt = method_pointer_traits<Method>;
if ( arg_count != mt::arity ) {
throw std::logic_error("an attempt to call a method with an incorrect arity");
}
return raw_method_invoke_impl<Method>(
method,
inst,
args,
std::make_index_sequence<mt::arity>());
}
template < typename Method >
method_invoke make_method_invoke(Method method) {
using namespace std::placeholders;
return std::bind(&raw_method_invoke<Method>, method, _1, _2, _3);
}
}
namespace meta_hpp::detail
{
using method_is_invocable_with = std::function<bool(const inst_base&, const arg_base*, std::size_t)>;
template < typename Method, std::size_t... Is >
bool raw_method_is_invocable_with_impl(
const inst_base& inst_base,
const arg_base* arg_bases,
std::index_sequence<Is...>)
{
using mt = method_pointer_traits<Method>;
using qualified_type = typename mt::qualified_type;
using argument_types = typename mt::argument_types;
return inst_base.can_cast_to<qualified_type>()
&& (... && (arg_bases + Is)->can_cast_to<std::tuple_element_t<Is, argument_types>>() );
}
template < typename Method >
bool raw_method_is_invocable_with(
const inst_base& inst_base,
const arg_base* arg_bases,
std::size_t arg_count)
{
using mt = method_pointer_traits<Method>;
if ( arg_count != mt::arity ) {
return false;
}
return raw_method_is_invocable_with_impl<Method>(
inst_base,
arg_bases,
std::make_index_sequence<mt::arity>());
}
template < typename Method >
method_is_invocable_with make_method_is_invocable_with() {
using namespace std::placeholders;
return std::bind(&raw_method_is_invocable_with<Method>, _1, _2, _3);
}
}
namespace meta_hpp
{
struct method_info::state final {
std::string name;
method_type type;
data_info_map datas;
detail::method_invoke invoke;
detail::method_is_invocable_with is_invocable_with;
};
}
@@ -67,6 +182,18 @@ namespace meta_hpp
inline const method_type& method_info::type() const noexcept {
return state_->type;
}
template < typename Instance, typename... Args >
std::optional<value> method_info::invoke(Instance&& instance, Args&&... args) const {
std::array<arg, sizeof...(Args)> vargs{arg{std::forward<Args>(args)}...};
return state_->invoke(inst{std::forward<Instance>(instance)}, vargs.data(), vargs.size());
}
template < typename Inst, typename... Args >
bool method_info::is_invocable_with() const noexcept {
std::array<arg_base, sizeof...(Args)> arg_bases{arg_base{typename_arg<Args>}...};
return state_->is_invocable_with(inst_base{typename_arg<Inst>}, arg_bases.data(), arg_bases.size());
}
}
namespace meta_hpp
@@ -91,12 +218,12 @@ namespace meta_hpp
namespace meta_hpp
{
template < typename Method >
method_info::method_info(std::string name, Method instance)
method_info::method_info(std::string name, Method method)
: state_{std::make_shared<state>(state{
std::move(name),
type_db::get<Method>().template as<method_type>(),
{}
})} {
(void)instance;
}
{},
detail::make_method_invoke<Method>(method),
detail::make_method_is_invocable_with<Method>(),
})} {}
}

View File

@@ -13,6 +13,9 @@ namespace meta_hpp
enum class method_flags : unsigned {
is_const = 1 << 0,
is_noexcept = 1 << 1,
is_volatile = 1 << 2,
is_lvalue_qualified = 1 << 3,
is_rvalue_qualified = 1 << 4,
};
ENUM_HPP_OPERATORS_DECL(method_flags)
@@ -39,6 +42,9 @@ namespace meta_hpp
bitflags<method_flags> flags() const noexcept;
bool is_const() const noexcept;
bool is_noexcept() const noexcept;
bool is_volatile() const noexcept;
bool is_lvalue_qualified() const noexcept;
bool is_rvalue_qualified() const noexcept;
private:
struct state;
std::shared_ptr<state> state_;
@@ -56,6 +62,7 @@ namespace meta_hpp::detail
using class_type = C;
using return_type = R;
using qualified_type = C;
using argument_types = std::tuple<Args...>;
static any_type make_class_type() {
@@ -77,6 +84,7 @@ namespace meta_hpp::detail
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const;
}
@@ -84,6 +92,7 @@ namespace meta_hpp::detail
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_noexcept;
}
@@ -91,10 +100,173 @@ namespace meta_hpp::detail
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const | method_flags::is_noexcept;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) &> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) & noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_noexcept | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const &> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const & noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const | method_flags::is_noexcept | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) &&> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) && noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_noexcept | method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const &&> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const | method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) const && noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_const | method_flags::is_noexcept | method_flags::is_rvalue_qualified;
}
};
//
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_noexcept;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const | method_flags::is_noexcept;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile &> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile & noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_noexcept | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const &> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const & noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const | method_flags::is_noexcept | method_flags::is_lvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile &&> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile && noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_noexcept | method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const &&> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const | method_flags::is_rvalue_qualified;
}
};
template < typename R, typename C, typename... Args >
struct method_pointer_traits<R(C::*)(Args...) volatile const && noexcept> : method_pointer_traits<R(C::*)(Args...)> {
using qualified_type = const C&&;
static bitflags<method_flags> make_flags() noexcept {
return method_flags::is_volatile | method_flags::is_const | method_flags::is_noexcept | method_flags::is_rvalue_qualified;
}
};
}
namespace meta_hpp
@@ -153,4 +325,16 @@ namespace meta_hpp
inline bool method_type::is_noexcept() const noexcept {
return state_->flags.has(method_flags::is_noexcept);
}
inline bool method_type::is_volatile() const noexcept {
return state_->flags.has(method_flags::is_volatile);
}
inline bool method_type::is_lvalue_qualified() const noexcept {
return state_->flags.has(method_flags::is_lvalue_qualified);
}
inline bool method_type::is_rvalue_qualified() const noexcept {
return state_->flags.has(method_flags::is_rvalue_qualified);
}
}

View File

@@ -9,4 +9,5 @@
#include "meta_fwd.hpp"
#include "meta_utilities/arg.hpp"
#include "meta_utilities/inst.hpp"
#include "meta_utilities/value.hpp"

View File

@@ -0,0 +1,183 @@
/*******************************************************************************
* 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"
namespace meta_hpp
{
class inst_base {
public:
enum class ref_types {
ref,
rref,
cref,
crref,
};
public:
inst_base() = delete;
inst_base(inst_base&&) = delete;
inst_base& operator=(inst_base&&) = delete;
inst_base(const inst_base&) = delete;
inst_base& operator=(const inst_base&) = delete;
template < typename T, std::enable_if_t<
std::is_lvalue_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>
, int> = 0>
explicit inst_base(typename_arg_t<T>);
template < typename T, std::enable_if_t<
std::is_class_v<T> ||
(std::is_rvalue_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>)
, int> = 0>
explicit inst_base(typename_arg_t<T>);
bool is_const() const noexcept;
bool is_lvalue() const noexcept;
bool is_rvalue() const noexcept;
any_type raw_type() const noexcept;
ref_types ref_type() const noexcept;
template < typename To >
bool can_cast_to() const noexcept;
private:
any_type raw_type_{};
ref_types ref_type_{};
};
}
namespace meta_hpp
{
template < typename T, std::enable_if_t<
std::is_lvalue_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>
, int> >
inst_base::inst_base(typename_arg_t<T>)
: raw_type_{type_db::get<stdex::remove_cvref_t<T>>()}
, ref_type_{std::is_const_v<std::remove_reference_t<T>> ? ref_types::cref : ref_types::ref} {}
template < typename T, std::enable_if_t<
std::is_class_v<T> ||
(std::is_rvalue_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>)
, int> >
inst_base::inst_base(typename_arg_t<T>)
: raw_type_{type_db::get<stdex::remove_cvref_t<T>>()}
, ref_type_{std::is_const_v<std::remove_reference_t<T>> ? ref_types::crref : ref_types::rref} {}
inline bool inst_base::is_const() const noexcept {
return ref_type_ == ref_types::cref
|| ref_type_ == ref_types::crref;
}
inline bool inst_base::is_lvalue() const noexcept {
return ref_type_ == ref_types::ref
|| ref_type_ == ref_types::cref;
}
inline bool inst_base::is_rvalue() const noexcept {
return ref_type_ == ref_types::rref
|| ref_type_ == ref_types::crref;
}
inline any_type inst_base::raw_type() const noexcept {
return raw_type_;
}
inline inst_base::ref_types inst_base::ref_type() const noexcept {
return ref_type_;
}
template < typename To >
bool inst_base::can_cast_to() const noexcept {
static_assert(
std::is_class_v<To> ||
(std::is_reference_v<To> && std::is_class_v<std::remove_reference_t<To>>));
constexpr bool to_const = std::is_const_v<std::remove_reference_t<To>>;
if ( !to_const && is_const() ) {
return false;
}
if constexpr ( std::is_lvalue_reference_v<To> ) {
if ( !is_lvalue() ) {
return false;
}
}
if constexpr ( std::is_rvalue_reference_v<To> ) {
if ( !is_rvalue() ) {
return false;
}
}
using to_raw_type = stdex::remove_cvref_t<To>;
return raw_type() == type_db::get<to_raw_type>();
}
}
namespace meta_hpp
{
class inst final : public inst_base {
public:
inst() = delete;
inst(inst&&) = delete;
inst& operator=(inst&&) = delete;
inst(const inst&) = delete;
inst& operator=(const inst&) = delete;
template < typename T, std::enable_if_t<
std::is_class_v<T> ||
(std::is_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>)
, int> = 0 >
explicit inst(T&& v);
template < typename To >
decltype(auto) cast() const;
private:
void* data_{};
};
}
namespace meta_hpp
{
template < typename T, std::enable_if_t<
std::is_class_v<T> ||
(std::is_reference_v<T> && std::is_class_v<std::remove_reference_t<T>>)
, int> >
inst::inst(T&& v)
: inst_base{typename_arg<T&&>}
, data_{const_cast<stdex::remove_cvref_t<T>*>(std::addressof(v))} {}
template < typename To >
decltype(auto) inst::cast() const {
if ( !can_cast_to<To>() ) {
throw std::logic_error("bad inst cast");
}
if constexpr ( std::is_reference_v<To> ) {
using raw_type_with_cv = std::remove_reference_t<To>;
if constexpr ( std::is_lvalue_reference_v<To> ) {
return *static_cast<raw_type_with_cv*>(data_);
}
if constexpr ( std::is_rvalue_reference_v<To> ) {
return std::move(*static_cast<raw_type_with_cv*>(data_));
}
}
if constexpr ( !std::is_reference_v<To>) {
using raw_type_with_cv = To;
return *static_cast<raw_type_with_cv*>(data_);
}
}
}

View File

@@ -21,7 +21,6 @@ namespace
class derived final : public base {
public:
derived() = default;
};
struct empty final {};

View File

@@ -23,6 +23,10 @@ namespace
[[maybe_unused]] explicit ivec2(int v): x{v}, y{v} {}
[[maybe_unused]] ivec2(int x, int y): x{x}, y{y} {}
};
[[maybe_unused]] bool operator==(const ivec2& l, const ivec2& r) noexcept {
return l.x == r.x && l.y == r.y;
}
}
TEST_CASE("features/infos/ctor") {
@@ -57,6 +61,22 @@ TEST_CASE("features/infos/ctor") {
CHECK(ci.type().id() == ci2.type().id());
CHECK(ci.type().id() == ci2.type().id());
}
{
CHECK(ci.is_invocable_with<>());
CHECK_FALSE(ci.is_invocable_with<int>());
CHECK_FALSE(ci.is_invocable_with<const int>());
CHECK_FALSE(ci.is_invocable_with<int&>());
CHECK_FALSE(ci.is_invocable_with<const int&>());
CHECK_FALSE(ci.is_invocable_with<int&&>());
CHECK_FALSE(ci.is_invocable_with<const int&&>());
}
{
CHECK(ci.invoke().equals(ivec2{}));
CHECK_THROWS(ci.invoke(42));
}
}
SUBCASE("int") {
@@ -76,6 +96,22 @@ TEST_CASE("features/infos/ctor") {
CHECK(ci.type().id() == ci2.type().id());
CHECK(ci.type().id() == ci2.type().id());
}
{
CHECK_FALSE(ci.is_invocable_with<>());
CHECK(ci.is_invocable_with<int>());
CHECK(ci.is_invocable_with<const int>());
CHECK(ci.is_invocable_with<int&>());
CHECK(ci.is_invocable_with<const int&>());
CHECK(ci.is_invocable_with<int&&>());
CHECK(ci.is_invocable_with<const int&&>());
}
{
CHECK_THROWS(ci.invoke());
CHECK(ci.invoke(42).equals(ivec2{42}));
}
}
SUBCASE("const ivec2&") {
@@ -95,6 +131,29 @@ TEST_CASE("features/infos/ctor") {
CHECK(ci.type().id() == ci2.type().id());
CHECK(ci.type().id() == ci2.type().id());
}
{
CHECK_FALSE(ci.is_invocable_with<>());
CHECK_FALSE(ci.is_invocable_with<int>());
CHECK_FALSE(ci.is_invocable_with<const int>());
CHECK_FALSE(ci.is_invocable_with<int&>());
CHECK_FALSE(ci.is_invocable_with<const int&>());
CHECK_FALSE(ci.is_invocable_with<int&&>());
CHECK_FALSE(ci.is_invocable_with<const int&&>());
CHECK(ci.is_invocable_with<ivec2>());
CHECK(ci.is_invocable_with<ivec2&>());
CHECK(ci.is_invocable_with<const ivec2&>());
CHECK(ci.is_invocable_with<ivec2&&>());
CHECK(ci.is_invocable_with<const ivec2&&>());
}
{
CHECK_THROWS(ci.invoke());
CHECK_THROWS(ci.invoke(42));
CHECK(ci.invoke(ivec2{21,42}).equals(ivec2{21,42}));
}
}
SUBCASE("int,int") {
@@ -116,5 +175,27 @@ TEST_CASE("features/infos/ctor") {
CHECK(ci.type().id() == ci2.type().id());
CHECK(ci.type().id() == ci2.type().id());
}
{
CHECK_FALSE(ci.is_invocable_with<>());
CHECK_FALSE(ci.is_invocable_with<int>());
CHECK_FALSE(ci.is_invocable_with<const int>());
CHECK_FALSE(ci.is_invocable_with<int&>());
CHECK_FALSE(ci.is_invocable_with<const int&>());
CHECK_FALSE(ci.is_invocable_with<int&&>());
CHECK_FALSE(ci.is_invocable_with<const int&&>());
CHECK(ci.is_invocable_with<int, int>());
CHECK(ci.is_invocable_with<int&, int&&>());
CHECK(ci.is_invocable_with<const int&, const int&&>());
}
{
CHECK_THROWS(ci.invoke());
CHECK_THROWS(ci.invoke(42));
CHECK_THROWS(ci.invoke(ivec2{21,42}));
CHECK(ci.invoke(21,42).equals(ivec2{21,42}));
}
}
}

View File

@@ -8,7 +8,88 @@
namespace
{
using namespace meta_hpp;
using namespace std::string_literals;
}
namespace
{
struct ivec2 {
int x{};
int y{};
[[maybe_unused]] ivec2() = default;
[[maybe_unused]] ivec2(const ivec2& other) = default;
[[maybe_unused]] explicit ivec2(int v): x{v}, y{v} {}
[[maybe_unused]] ivec2(int x, int y): x{x}, y{y} {}
};
bool operator==(const ivec2& l, const ivec2& r) noexcept {
return l.x == r.x && l.y == r.y;
}
ivec2 iadd(const ivec2& l, const ivec2& r) noexcept {
return {l.x + r.x, l.y + r.y};
}
int ilength2(const ivec2& v) noexcept {
return v.x * v.x + v.y * v.y;
}
}
TEST_CASE("features/infos/function") {
registry db;
db(
namespace_{"vmath"}(
class_<ivec2>{"ivec2"},
function_{"iadd", &iadd}(data_{"info", "iadd function"s}),
function_{"ilength2", &ilength2}(data_{"info", "ilength2 function"s})
)
);
SUBCASE("iadd") {
const function_info fi = db.get_function_by_path("vmath::iadd");
REQUIRE(fi);
REQUIRE(fi.get_data_by_name("info"));
CHECK(fi.get_data_by_name("info").value().equals("iadd function"s));
CHECK_FALSE(fi.is_invocable_with());
CHECK_FALSE(fi.is_invocable_with<int>());
CHECK_FALSE(fi.is_invocable_with<ivec2, int>());
CHECK_FALSE(fi.is_invocable_with<ivec2, ivec2, int>());
CHECK(fi.is_invocable_with<ivec2, ivec2>());
CHECK(fi.is_invocable_with<ivec2&&, const ivec2&>());
CHECK_THROWS(fi.invoke());
CHECK_THROWS(fi.invoke(42));
CHECK_THROWS(fi.invoke(ivec2{}, 42));
CHECK(fi.invoke(ivec2{1,2},ivec2{3,4}));
CHECK(fi.invoke(ivec2{1,2},ivec2{3,4})->equals(ivec2{4,6}));
}
SUBCASE("ilength2") {
const function_info fi = db.get_function_by_path("vmath::ilength2");
REQUIRE(fi);
REQUIRE(fi.get_data_by_name("info"));
CHECK(fi.get_data_by_name("info").value().equals("ilength2 function"s));
CHECK_FALSE(fi.is_invocable_with());
CHECK_FALSE(fi.is_invocable_with<int>());
CHECK_FALSE(fi.is_invocable_with<ivec2, int>());
CHECK(fi.is_invocable_with<ivec2>());
CHECK(fi.is_invocable_with<const ivec2&>());
CHECK_THROWS(fi.invoke());
CHECK_THROWS(fi.invoke(42));
CHECK_THROWS(fi.invoke(ivec2{}, 42));
CHECK(fi.invoke(ivec2{2,3}));
CHECK(fi.invoke(ivec2{2,3})->equals(13));
}
}

View File

@@ -8,7 +8,779 @@
namespace
{
using namespace meta_hpp;
using namespace std::string_literals;
}
namespace
{
struct clazz {
clazz() = default;
clazz(clazz&&) = delete;
clazz(const clazz&) = delete;
clazz& operator=(clazz&&) = delete;
clazz& operator=(const clazz&) = delete;
//
int non_const_method() { return 1; }
int non_const_method_noexcept() noexcept { return 2; }
int const_method() const { return 3; }
int const_method_noexcept() const noexcept { return 4; }
int non_const_method_ref() & { return 5; }
int non_const_method_noexcept_ref() & noexcept { return 6; }
int const_method_ref() const & { return 7; }
int const_method_noexcept_ref() const & noexcept { return 8; }
int non_const_method_rref() && { return 9; }
int non_const_method_noexcept_rref() && noexcept { return 10; }
int const_method_rref() const && { return 11; }
int const_method_noexcept_rref() const && noexcept { return 12; }
//
int non_const_method_volatile() volatile { return 1; }
int non_const_method_noexcept_volatile() volatile noexcept { return 2; }
int const_method_volatile() volatile const { return 3; }
int const_method_noexcept_volatile() volatile const noexcept { return 4; }
int non_const_method_ref_volatile() volatile & { return 5; }
int non_const_method_noexcept_ref_volatile() volatile & noexcept { return 6; }
int const_method_ref_volatile() volatile const & { return 7; }
int const_method_noexcept_ref_volatile() volatile const & noexcept { return 8; }
int non_const_method_rref_volatile() volatile && { return 9; }
int non_const_method_noexcept_rref_volatile() volatile && noexcept { return 10; }
int const_method_rref_volatile() volatile const && { return 11; }
int const_method_noexcept_rref_volatile() volatile const && noexcept { return 12; }
};
struct clazz2 {};
}
TEST_CASE("features/infos/method") {
registry db;
db(
class_<clazz>{"clazz"}(
method_{"non_const_method", &clazz::non_const_method},
method_{"non_const_method_noexcept", &clazz::non_const_method_noexcept},
method_{"const_method", &clazz::const_method},
method_{"const_method_noexcept", &clazz::const_method_noexcept},
method_{"non_const_method_ref", &clazz::non_const_method_ref},
method_{"non_const_method_noexcept_ref", &clazz::non_const_method_noexcept_ref},
method_{"const_method_ref", &clazz::const_method_ref},
method_{"const_method_noexcept_ref", &clazz::const_method_noexcept_ref},
method_{"non_const_method_rref", &clazz::non_const_method_rref},
method_{"non_const_method_noexcept_rref", &clazz::non_const_method_noexcept_rref},
method_{"const_method_rref", &clazz::const_method_rref},
method_{"const_method_noexcept_rref", &clazz::const_method_noexcept_rref},
//
method_{"non_const_method_volatile", &clazz::non_const_method_volatile},
method_{"non_const_method_noexcept_volatile", &clazz::non_const_method_noexcept_volatile},
method_{"const_method_volatile", &clazz::const_method_volatile},
method_{"const_method_noexcept_volatile", &clazz::const_method_noexcept_volatile},
method_{"non_const_method_ref_volatile", &clazz::non_const_method_ref_volatile},
method_{"non_const_method_noexcept_ref_volatile", &clazz::non_const_method_noexcept_ref_volatile},
method_{"const_method_ref_volatile", &clazz::const_method_ref_volatile},
method_{"const_method_noexcept_ref_volatile", &clazz::const_method_noexcept_ref_volatile},
method_{"non_const_method_rref_volatile", &clazz::non_const_method_rref_volatile},
method_{"non_const_method_noexcept_rref_volatile", &clazz::non_const_method_noexcept_rref_volatile},
method_{"const_method_rref_volatile", &clazz::const_method_rref_volatile},
method_{"const_method_noexcept_rref_volatile", &clazz::const_method_noexcept_rref_volatile}
)
);
SUBCASE("non_const_method") {
const method_info mi = db.get_method_by_path("clazz::non_const_method");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == method_flags{});
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == method_flags::is_volatile);
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(1));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(1));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(1));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(1));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_volatile), const clazz&&>);
}
SUBCASE("non_const_method_noexcept") {
const method_info mi = db.get_method_by_path("clazz::non_const_method_noexcept");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_noexcept_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == method_flags::is_noexcept);
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_noexcept | method_flags::is_volatile));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(2));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(2));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(2));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(2));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_volatile), const clazz&&>);
}
SUBCASE("const_method") {
const method_info mi = db.get_method_by_path("clazz::const_method");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == method_flags::is_const);
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_volatile));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(3));
CHECK(mi.invoke(std::as_const(cl))->equals(3));
CHECK(mi.invoke(std::move(cl))->equals(3));
CHECK(mi.invoke(std::move(std::as_const(cl)))->equals(3));
CHECK(mi2.invoke(cl)->equals(3));
CHECK(mi2.invoke(std::as_const(cl))->equals(3));
CHECK(mi2.invoke(std::move(cl))->equals(3));
CHECK(mi2.invoke(std::move(std::as_const(cl)))->equals(3));
}
static_assert(std::is_invocable_v<decltype(&clazz::const_method), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_volatile), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_volatile), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_volatile), const clazz&&>);
}
SUBCASE("const_method_noexcept") {
const method_info mi = db.get_method_by_path("clazz::const_method_noexcept");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_noexcept_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_const | method_flags::is_noexcept));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_noexcept | method_flags::is_volatile));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(4));
CHECK(mi.invoke(std::as_const(cl))->equals(4));
CHECK(mi.invoke(std::move(cl))->equals(4));
CHECK(mi.invoke(std::move(std::as_const(cl)))->equals(4));
CHECK(mi2.invoke(cl)->equals(4));
CHECK(mi2.invoke(std::as_const(cl))->equals(4));
CHECK(mi2.invoke(std::move(cl))->equals(4));
CHECK(mi2.invoke(std::move(std::as_const(cl)))->equals(4));
}
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_volatile), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_volatile), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_volatile), const clazz&&>);
}
SUBCASE("non_const_method_ref") {
const method_info mi = db.get_method_by_path("clazz::non_const_method_ref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_ref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == method_flags::is_lvalue_qualified);
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_volatile | method_flags::is_lvalue_qualified));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK_FALSE(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(5));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK_THROWS(mi.invoke(std::move(cl)));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(5));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK_THROWS(mi2.invoke(std::move(cl)));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_ref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_ref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref_volatile), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_ref_volatile), const clazz&&>);
}
SUBCASE("non_const_method_noexcept_ref") {
const method_info mi = db.get_method_by_path("clazz::non_const_method_noexcept_ref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_noexcept_ref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_noexcept | method_flags::is_lvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_noexcept | method_flags::is_volatile | method_flags::is_lvalue_qualified));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK_FALSE(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(6));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK_THROWS(mi.invoke(std::move(cl)));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(6));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK_THROWS(mi2.invoke(std::move(cl)));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref_volatile), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_ref_volatile), const clazz&&>);
}
SUBCASE("const_method_ref") {
const method_info mi = db.get_method_by_path("clazz::const_method_ref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_ref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_const | method_flags::is_lvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_volatile | method_flags::is_lvalue_qualified));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK(mi.is_invocable_with<const clazz&>());
CHECK_FALSE(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK(mi2.is_invocable_with<const clazz&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(7));
CHECK(mi.invoke(std::as_const(cl))->equals(7));
CHECK_THROWS(mi.invoke(std::move(cl)));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(7));
CHECK(mi2.invoke(std::as_const(cl))->equals(7));
CHECK_THROWS(mi2.invoke(std::move(cl)));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::const_method_ref), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_ref), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_ref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_ref), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_ref_volatile), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_ref_volatile), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_ref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_ref_volatile), const clazz&&>);
}
SUBCASE("const_method_noexcept_ref") {
const method_info mi = db.get_method_by_path("clazz::const_method_noexcept_ref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_noexcept_ref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_const | method_flags::is_noexcept | method_flags::is_lvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_noexcept | method_flags::is_volatile | method_flags::is_lvalue_qualified));
}
{
CHECK(mi.is_invocable_with<clazz&>());
CHECK(mi.is_invocable_with<const clazz&>());
CHECK_FALSE(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK(mi2.is_invocable_with<clazz&>());
CHECK(mi2.is_invocable_with<const clazz&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK(mi.invoke(cl)->equals(8));
CHECK(mi.invoke(std::as_const(cl))->equals(8));
CHECK_THROWS(mi.invoke(std::move(cl)));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK(mi2.invoke(cl)->equals(8));
CHECK(mi2.invoke(std::as_const(cl))->equals(8));
CHECK_THROWS(mi2.invoke(std::move(cl)));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref), const clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref_volatile), clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref_volatile), const clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_ref_volatile), const clazz&&>);
}
SUBCASE("non_const_method_rref") {
const method_info mi = db.get_method_by_path("clazz::non_const_method_rref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_rref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == method_flags::is_rvalue_qualified);
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_volatile | method_flags::is_rvalue_qualified));
}
{
CHECK_FALSE(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK_THROWS(mi.invoke(cl));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(9));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK_THROWS(mi2.invoke(cl));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(9));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_rref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref), const clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_rref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_rref_volatile), const clazz&&>);
}
SUBCASE("non_const_method_noexcept_rref") {
const method_info mi = db.get_method_by_path("clazz::non_const_method_noexcept_rref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::non_const_method_noexcept_rref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_noexcept | method_flags::is_rvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_noexcept | method_flags::is_volatile | method_flags::is_rvalue_qualified));
}
{
CHECK_FALSE(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK_THROWS(mi.invoke(cl));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(10));
CHECK_THROWS(mi.invoke(std::move(std::as_const(cl))));
CHECK_THROWS(mi2.invoke(cl));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(10));
CHECK_THROWS(mi2.invoke(std::move(std::as_const(cl))));
}
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref), const clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref_volatile), clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::non_const_method_noexcept_rref_volatile), const clazz&&>);
}
SUBCASE("const_method_rref") {
const method_info mi = db.get_method_by_path("clazz::const_method_rref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_rref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_const | method_flags::is_rvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_volatile | method_flags::is_rvalue_qualified));
}
{
CHECK_FALSE(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK(mi.is_invocable_with<const clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK_THROWS(mi.invoke(cl));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(11));
CHECK(mi.invoke(std::move(std::as_const(cl)))->equals(11));
CHECK_THROWS(mi2.invoke(cl));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(11));
CHECK(mi2.invoke(std::move(std::as_const(cl)))->equals(11));
}
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_rref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_rref), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_rref), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_rref), const clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_rref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_rref_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_rref_volatile), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_rref_volatile), const clazz&&>);
}
SUBCASE("const_method_noexcept_rref") {
const method_info mi = db.get_method_by_path("clazz::const_method_noexcept_rref");
REQUIRE(mi);
const method_info mi2 = db.get_method_by_path("clazz::const_method_noexcept_rref_volatile");
REQUIRE(mi2);
{
CHECK(mi.type().arity() == 0);
CHECK(mi.type().class_type() == type_db::get<clazz>());
CHECK(mi.type().return_type() == type_db::get<int>());
CHECK(mi.type().flags() == (method_flags::is_const | method_flags::is_noexcept | method_flags::is_rvalue_qualified));
CHECK(mi2.type().arity() == 0);
CHECK(mi2.type().class_type() == type_db::get<clazz>());
CHECK(mi2.type().return_type() == type_db::get<int>());
CHECK(mi2.type().flags() == (method_flags::is_const | method_flags::is_noexcept | method_flags::is_volatile | method_flags::is_rvalue_qualified));
}
{
CHECK_FALSE(mi.is_invocable_with<clazz&>());
CHECK_FALSE(mi.is_invocable_with<const clazz&>());
CHECK(mi.is_invocable_with<clazz&&>());
CHECK(mi.is_invocable_with<const clazz&&>());
CHECK_FALSE(mi2.is_invocable_with<clazz&>());
CHECK_FALSE(mi2.is_invocable_with<const clazz&>());
CHECK(mi2.is_invocable_with<clazz&&>());
CHECK(mi2.is_invocable_with<const clazz&&>());
}
{
clazz cl;
CHECK_THROWS(mi.invoke(cl));
CHECK_THROWS(mi.invoke(std::as_const(cl)));
CHECK(mi.invoke(std::move(cl))->equals(12));
CHECK(mi.invoke(std::move(std::as_const(cl)))->equals(12));
CHECK_THROWS(mi2.invoke(cl));
CHECK_THROWS(mi2.invoke(std::as_const(cl)));
CHECK(mi2.invoke(std::move(cl))->equals(12));
CHECK(mi2.invoke(std::move(std::as_const(cl)))->equals(12));
}
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref), const clazz&&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref_volatile), clazz&>);
static_assert(!std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref_volatile), const clazz&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref_volatile), clazz&&>);
static_assert(std::is_invocable_v<decltype(&clazz::const_method_noexcept_rref_volatile), const clazz&&>);
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* 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)
******************************************************************************/
#include "_utilities_fwd.hpp"
namespace
{
using namespace meta_hpp;
using namespace std::string_literals;
}
namespace
{
struct ivec2 {
int x{};
int y{};
[[maybe_unused]] ivec2() = default;
[[maybe_unused]] explicit ivec2(int v): x{v}, y{v} {}
[[maybe_unused]] ivec2(int x, int y): x{x}, y{y} {}
[[maybe_unused]] ivec2(ivec2&& other) noexcept {
x = other.x;
y = other.y;
other.x = 0;
other.y = 0;
}
[[maybe_unused]] ivec2(const ivec2& other) noexcept {
x = other.x;
y = other.y;
}
ivec2& operator=(ivec2&&) = delete;
ivec2& operator=(const ivec2&) = delete;
};
}
TEST_CASE("features/utilities/inst/type") {
SUBCASE("ref") {
ivec2 v{1,2};
ivec2& vr = v;
inst a{vr};
CHECK(a.raw_type() == type_db::get<ivec2>());
CHECK(a.ref_type() == inst::ref_types::ref);
}
SUBCASE("cref") {
const ivec2 v{1,2};
const ivec2& vr = v;
inst a{vr};
CHECK(a.raw_type() == type_db::get<ivec2>());
CHECK(a.ref_type() == inst::ref_types::cref);
}
SUBCASE("rref") {
ivec2 v{1,2};
inst a{std::move(v)};
CHECK(a.raw_type() == type_db::get<ivec2>());
CHECK(a.ref_type() == inst::ref_types::rref);
}
SUBCASE("crref") {
const ivec2 v{1,2};
inst a{std::move(v)};
CHECK(a.raw_type() == type_db::get<ivec2>());
CHECK(a.ref_type() == inst::ref_types::crref);
}
}

View File

@@ -10,14 +10,17 @@ namespace
{
using namespace meta_hpp;
using namespace std::string_literals;
}
namespace
{
struct ivec2 {
int x{};
int y{};
ivec2() = default;
explicit ivec2(int v): x{v}, y{v} {}
ivec2(int x, int y): x{x}, y{y} {}
[[maybe_unused]] ivec2() = default;
[[maybe_unused]] explicit ivec2(int v): x{v}, y{v} {}
[[maybe_unused]] ivec2(int x, int y): x{x}, y{y} {}
ivec2(ivec2&& other) noexcept
: x{other.x}