diff --git a/headers/meta.hpp/meta.hpp b/headers/meta.hpp/meta.hpp index bae8590..5a55fac 100644 --- a/headers/meta.hpp/meta.hpp +++ b/headers/meta.hpp/meta.hpp @@ -6,6 +6,14 @@ #pragma once -namespace meta_hpp -{ -} +#include "meta_fwd.hpp" + +#include "meta_value.hpp" +#include "meta_registry.hpp" + +#include "meta_class.hpp" +#include "meta_field.hpp" +#include "meta_function.hpp" +#include "meta_method.hpp" +#include "meta_namespace.hpp" +#include "meta_variable.hpp" diff --git a/headers/meta.hpp/meta_class.hpp b/headers/meta.hpp/meta_class.hpp new file mode 100644 index 0000000..134431f --- /dev/null +++ b/headers/meta.hpp/meta_class.hpp @@ -0,0 +1,141 @@ +/******************************************************************************* + * 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_field.hpp" +#include "meta_function.hpp" +#include "meta_method.hpp" +#include "meta_variable.hpp" + +namespace meta_hpp +{ + class class_info { + public: + class_info() = delete; + + class_info(class_info&&) = default; + class_info(const class_info&) = default; + + class_info& operator=(class_info&&) = default; + class_info& operator=(const class_info&) = default; + + class_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + + std::optional get_class(std::string_view id) const { + return detail::find_opt(classes_, id); + } + + std::optional get_field(std::string_view id) const { + return detail::find_opt(fields_, id); + } + + std::optional get_function(std::string_view id) const { + return detail::find_opt(functions_, id); + } + + std::optional get_method(std::string_view id) const { + return detail::find_opt(methods_, id); + } + + std::optional get_variable(std::string_view id) const { + return detail::find_opt(variables_, id); + } + private: + friend class namespace_info; + template < typename Class > friend class class_; + friend class namespace_; + private: + void merge_with_(const class_info& other) { + detail::merge_with(classes_, other.classes_, &class_info::merge_with_); + detail::merge_with(fields_, other.fields_, &field_info::merge_with_); + detail::merge_with(functions_, other.functions_, &function_info::merge_with_); + detail::merge_with(methods_, other.methods_, &method_info::merge_with_); + detail::merge_with(variables_, other.variables_, &variable_info::merge_with_); + } + private: + std::string id_; + std::map> classes_; + std::map> fields_; + std::map> functions_; + std::map> methods_; + std::map> variables_; + }; +} + +namespace meta_hpp +{ + template < typename Class > + class class_ { + public: + explicit class_(std::string id) + : info_(std::move(id)) {} + + const class_info& info() const noexcept { + return info_; + } + + template < typename... Internals > + class_& operator()(Internals&&...internals) { + (add_(std::forward(internals)), ...); + return *this; + } + private: + template < typename InternalClass > + void add_(const class_& internal) { + detail::merge_with( + info_.classes_, + internal.info().id(), + internal.info(), + &class_info::merge_with_); + } + + template < auto InternalField > + void add_(const field_& internal) { + detail::merge_with( + info_.fields_, + internal.info().id(), + internal.info(), + &field_info::merge_with_); + } + + template < auto InternalFunction > + void add_(const function_& internal) { + detail::merge_with( + info_.functions_, + internal.info().id(), + internal.info(), + &function_info::merge_with_); + } + + template < auto InternalMethod > + void add_(const method_& internal) { + detail::merge_with( + info_.methods_, + internal.info().id(), + internal.info(), + &method_info::merge_with_); + } + + template < auto InternalVariable > + void add_(const variable_& internal) { + detail::merge_with( + info_.variables_, + internal.info().id(), + internal.info(), + &variable_info::merge_with_); + } + private: + class_info info_; + }; +} diff --git a/headers/meta.hpp/meta_field.hpp b/headers/meta.hpp/meta_field.hpp new file mode 100644 index 0000000..e42cce6 --- /dev/null +++ b/headers/meta.hpp/meta_field.hpp @@ -0,0 +1,120 @@ +/******************************************************************************* + * 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" + +namespace meta_hpp::field_detail +{ + template < typename Field > + struct field_traits; + + template < typename T, typename Base > + struct field_traits { + static constexpr bool is_const = false; + using value_type = T; + using instance_type = Base; + }; + + template < typename T, typename Base > + struct field_traits { + static constexpr bool is_const = true; + using value_type = T; + using instance_type = Base; + }; + + template < auto Field > + value getter(const void* instance) { + using ft = field_traits; + using value_type = typename ft::value_type; + using instance_type = typename ft::instance_type; + + auto instance_ptr = static_cast(instance); + value_type typed_value = std::invoke(Field, *instance_ptr); + + return value{std::move(typed_value)}; + } + + template < auto Field > + void setter(void* instance, value value) { + using ft = field_traits; + using value_type = typename ft::value_type; + using instance_type = typename ft::instance_type; + + if constexpr ( !ft::is_const ) { + auto instance_ptr = static_cast(instance); + std::invoke(Field, *instance_ptr) = value.cast(); + } else { + throw std::logic_error("an attempt to change a constant field"); + } + } +} + +namespace meta_hpp +{ + class field_info { + public: + field_info() = delete; + + field_info(field_info&&) = default; + field_info(const field_info&) = default; + + field_info& operator=(field_info&&) = default; + field_info& operator=(const field_info&) = default; + + field_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + public: + value get(const void* instance) const { + return getter_(instance); + } + + template < typename Value > + void set(void* instance, Value&& value) const { + return setter_(instance, std::forward(value)); + } + private: + friend class class_info; + template < typename Class > friend class class_; + template < auto Field > friend class field_; + private: + void merge_with_(const field_info& other) { + (void)other; + } + private: + std::string id_; + value(*getter_)(const void*); + void(*setter_)(void*, value); + }; +} + +namespace meta_hpp +{ + template < auto Field > + class field_ { + public: + static_assert(std::is_member_object_pointer_v); + + explicit field_(std::string id) + : info_(std::move(id)) { + info_.getter_ = &field_detail::getter; + info_.setter_ = &field_detail::setter; + } + + const field_info& info() const noexcept { + return info_; + } + private: + field_info info_; + }; +} diff --git a/headers/meta.hpp/meta_function.hpp b/headers/meta.hpp/meta_function.hpp new file mode 100644 index 0000000..81680f9 --- /dev/null +++ b/headers/meta.hpp/meta_function.hpp @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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" + +namespace meta_hpp::function_detail +{ + template < typename Function > + struct function_traits; + + template < typename R, typename... Args > + struct function_traits { + static constexpr std::size_t arity = sizeof...(Args); + using return_type = R; + using argument_types = std::tuple; + }; + + template < typename R, typename... Args > + struct function_traits + : function_traits {}; + + template < auto Function, std::size_t... Is > + value invoke(value* args, std::index_sequence) { + using ft = function_traits; + using return_type = typename ft::return_type; + using argument_types = typename ft::argument_types; + + auto typed_arguments = std::make_tuple( + (args + Is)->try_cast>()...); + + if ( !(std::get(typed_arguments) && ...) ) { + throw std::logic_error("an attempt to call a function with incorrect argument types"); + } + + if constexpr ( std::is_void_v ) { + std::invoke(Function, + *std::get(typed_arguments)...); + return value{}; + } else { + return_type return_value = std::invoke(Function, + *std::get(typed_arguments)...); + return value{std::move(return_value)}; + } + } + + template < auto Function > + value invoke(value* args, std::size_t arg_count) { + using ft = function_traits; + + if ( arg_count != ft::arity ) { + throw std::logic_error("an attempt to call a function with an incorrect arity"); + } + + return invoke(args, std::make_index_sequence()); + } +} + +namespace meta_hpp +{ + class function_info { + public: + function_info() = delete; + + function_info(function_info&&) = default; + function_info(const function_info&) = default; + + function_info& operator=(function_info&&) = default; + function_info& operator=(const function_info&) = default; + + function_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + public: + template < typename... Args > + value invoke(Args&&... args) const { + std::array vargs{{std::forward(args)...}}; + return invoke_(vargs.data(), vargs.size()); + } + private: + friend class class_info; + friend class namespace_info; + + template < typename Class > friend class class_; + friend class namespace_; + + template < auto Function > friend class function_; + private: + void merge_with_(const function_info& other) { + (void)other; + } + private: + std::string id_; + value(*invoke_)(value*, std::size_t); + }; +} + +namespace meta_hpp +{ + template < auto Function > + class function_ { + public: + static_assert(std::is_function_v>); + + explicit function_(std::string id) + : info_(std::move(id)) { + info_.invoke_ = &function_detail::invoke; + } + + const function_info& info() const noexcept { + return info_; + } + private: + function_info info_; + }; +} diff --git a/headers/meta.hpp/meta_fwd.hpp b/headers/meta.hpp/meta_fwd.hpp new file mode 100644 index 0000000..cc304c8 --- /dev/null +++ b/headers/meta.hpp/meta_fwd.hpp @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace meta_hpp +{ + class value; + class registry; + + class class_info; + class field_info; + class function_info; + class method_info; + class namespace_info; + class variable_info; + + template < typename Class > + class class_; + + template < auto Field > + class field_; + + template < auto Function > + class function_; + + template < auto Method > + class method_; + + class namespace_; + + template < auto Variable > + class variable_; +} + +namespace meta_hpp +{ + template < typename Signature > + constexpr auto select(Signature* f) noexcept { + return f; + } + + template < typename Signature, typename Base > + constexpr auto select(Signature Base::*f) noexcept { + return f; + } + + template < typename R, typename Base, typename... Args > + constexpr auto select_const(R (Base::*f)(Args...) const) noexcept { + return f; + } + + template < typename R, typename Base, typename... Args > + constexpr auto select_non_const(R(Base::*f)(Args...)) noexcept { + return f; + } +} + +namespace meta_hpp::detail +{ + template < typename K, typename V, typename C, typename K2 > + std::optional find_opt(const std::map& src, K2&& key) { + if ( auto iter = src.find(key); iter != src.end() ) { + return iter->second; + } + return std::nullopt; + } + + template < typename K, typename V, typename C, typename K2, typename V2, typename F > + void merge_with(std::map& dst, K2&& key, V2&& value, F&& f) { + if ( auto iter = dst.find(key); iter != dst.end() ) { + std::invoke(std::forward(f), iter->second, std::forward(value)); + } else { + dst.emplace(std::forward(key), std::forward(value)); + } + } + + template < typename K, typename V, typename C, typename F > + void merge_with(std::map& dst, const std::map& src, F&& f) { + for ( auto [key, value] : src ) { + merge_with(dst, key, value, f); + } + } +} diff --git a/headers/meta.hpp/meta_method.hpp b/headers/meta.hpp/meta_method.hpp new file mode 100644 index 0000000..2d908ca --- /dev/null +++ b/headers/meta.hpp/meta_method.hpp @@ -0,0 +1,186 @@ +/******************************************************************************* + * 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" + +namespace meta_hpp::method_detail +{ + template < typename Method > + struct method_traits; + + template < typename R, typename Base, typename... Args > + struct method_traits { + 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; + }; + + template < typename R, typename Base, typename... Args > + struct method_traits + : method_traits { + static constexpr bool is_const = true; + }; + + template < typename R, typename Base, typename... Args > + struct method_traits + : method_traits {}; + + template < typename R, typename Base, typename... Args > + struct method_traits + : method_traits {}; + + template < auto Method, std::size_t... Is > + value invoke(void* instance, value* args, std::index_sequence) { + using mt = method_traits; + using return_type = typename mt::return_type; + using instance_type = typename mt::instance_type; + using argument_types = typename mt::argument_types; + + auto typed_arguments = std::make_tuple( + (args + Is)->try_cast>()...); + + if ( !(std::get(typed_arguments) && ...) ) { + throw std::logic_error("an attempt to call a method with incorrect argument types"); + } + + if constexpr ( std::is_void_v ) { + std::invoke(Method, + std::ref(*static_cast(instance)), + *std::get(typed_arguments)...); + return value{}; + } else { + return_type return_value = std::invoke(Method, + std::ref(*static_cast(instance)), + *std::get(typed_arguments)...); + return value{std::move(return_value)}; + } + } + + template < auto Method > + value invoke(void* instance, value* args, std::size_t arg_count) { + using mt = method_traits; + + if ( arg_count != mt::arity ) { + throw std::logic_error("an attempt to call a method with an incorrect arity"); + } + + return invoke(instance, args, std::make_index_sequence()); + } + + template < auto Method, std::size_t... Is > + value cinvoke(const void* instance, value* args, std::index_sequence) { + using mt = method_traits; + using return_type = typename mt::return_type; + using instance_type = typename mt::instance_type; + using argument_types = typename mt::argument_types; + + auto typed_arguments = std::make_tuple( + (args + Is)->try_cast>()...); + + if ( !(std::get(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 ) { + std::invoke(Method, + std::ref(*static_cast(instance)), + *std::get(typed_arguments)...); + return value{}; + } else { + return_type return_value = std::invoke(Method, + std::ref(*static_cast(instance)), + *std::get(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 > + value cinvoke(const void* instance, value* args, std::size_t arg_count) { + using mt = method_traits; + + if ( arg_count != mt::arity ) { + throw std::logic_error("an attempt to call a method with a different arity"); + } + + return cinvoke(instance, args, std::make_index_sequence()); + } +} + +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; + + method_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + public: + template < typename... Args > + value invoke(void* instance, Args&&... args) const { + std::array vargs{{std::forward(args)...}}; + return invoke_(instance, vargs.data(), vargs.size()); + } + + template < typename... Args > + value invoke(const void* instance, Args&&... args) const { + std::array vargs{{std::forward(args)...}}; + return cinvoke_(instance, vargs.data(), vargs.size()); + } + private: + friend class class_info; + template < typename Class > friend class class_; + template < auto Method > friend class method_; + private: + void merge_with_(const method_info& other) { + (void)other; + } + private: + std::string id_; + value(*invoke_)(void*, value*, std::size_t); + value(*cinvoke_)(const void*, value*, std::size_t); + }; +} + +namespace meta_hpp +{ + template < auto Method > + class method_ { + public: + static_assert(std::is_member_function_pointer_v); + + explicit method_(std::string id) + : info_(std::move(id)) { + info_.invoke_ = &method_detail::invoke; + info_.cinvoke_ = &method_detail::cinvoke; + } + + const method_info& info() const noexcept { + return info_; + } + private: + method_info info_; + }; +} diff --git a/headers/meta.hpp/meta_namespace.hpp b/headers/meta.hpp/meta_namespace.hpp new file mode 100644 index 0000000..895e1e4 --- /dev/null +++ b/headers/meta.hpp/meta_namespace.hpp @@ -0,0 +1,121 @@ +/******************************************************************************* + * 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_class.hpp" +#include "meta_function.hpp" +#include "meta_variable.hpp" + +namespace meta_hpp +{ + class namespace_info { + public: + namespace_info() = delete; + + namespace_info(namespace_info&&) = default; + namespace_info(const namespace_info&) = default; + + namespace_info& operator=(namespace_info&&) = default; + namespace_info& operator=(const namespace_info&) = default; + + namespace_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + + std::optional get_class(std::string_view id) const { + return detail::find_opt(classes_, id); + } + + std::optional get_function(std::string_view id) const { + return detail::find_opt(functions_, id); + } + + std::optional get_namespace(std::string_view id) const { + return detail::find_opt(namespaces_, id); + } + + std::optional get_variable(std::string_view id) const { + return detail::find_opt(variables_, id); + } + private: + friend class namespace_; + private: + void merge_with_(const namespace_info& other) { + detail::merge_with(classes_, other.classes_, &class_info::merge_with_); + detail::merge_with(functions_, other.functions_, &function_info::merge_with_); + detail::merge_with(namespaces_, other.namespaces_, &namespace_info::merge_with_); + detail::merge_with(variables_, other.variables_, &variable_info::merge_with_); + } + private: + std::string id_; + std::map> classes_; + std::map> functions_; + std::map> namespaces_; + std::map> variables_; + }; +} + +namespace meta_hpp +{ + class namespace_ { + public: + explicit namespace_(std::string id) + : info_(std::move(id)) {} + + const namespace_info& info() const noexcept { + return info_; + } + + template < typename... Internals > + namespace_& operator()(Internals&&...internals) { + (add_(std::forward(internals)), ...); + return *this; + } + private: + template < typename InternalClass > + void add_(const class_& internal) { + detail::merge_with( + info_.classes_, + internal.info().id(), + internal.info(), + &class_info::merge_with_); + } + + template < auto InternalFunction > + void add_(const function_& internal) { + detail::merge_with( + info_.functions_, + internal.info().id(), + internal.info(), + &function_info::merge_with_); + } + + void add_(const namespace_& internal) { + detail::merge_with( + info_.namespaces_, + internal.info().id(), + internal.info(), + &namespace_info::merge_with_); + } + + template < auto Internalvariable > + void add_(const variable_& internal) { + detail::merge_with( + info_.variables_, + internal.info().id(), + internal.info(), + &variable_info::merge_with_); + } + private: + namespace_info info_; + }; +} diff --git a/headers/meta.hpp/meta_registry.hpp b/headers/meta.hpp/meta_registry.hpp new file mode 100644 index 0000000..b2f43ce --- /dev/null +++ b/headers/meta.hpp/meta_registry.hpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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_class.hpp" +#include "meta_function.hpp" +#include "meta_namespace.hpp" +#include "meta_variable.hpp" + +namespace meta_hpp +{ + class registry { + public: + registry() = default; + + template < typename... Internals > + registry& operator()(Internals&&...internals) { + default_namespace_(std::forward(internals)...); + return *this; + } + + std::optional get_class(std::string_view id) const { + return default_namespace_.info().get_class(id); + } + + std::optional get_function(std::string_view id) const { + return default_namespace_.info().get_function(id); + } + + std::optional get_namespace(std::string_view id) const { + return default_namespace_.info().get_namespace(id); + } + + std::optional get_variable(std::string_view id) const { + return default_namespace_.info().get_variable(id); + } + private: + namespace_ default_namespace_{""}; + }; +} diff --git a/headers/meta.hpp/meta_value.hpp b/headers/meta.hpp/meta_value.hpp new file mode 100644 index 0000000..b059626 --- /dev/null +++ b/headers/meta.hpp/meta_value.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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 value { + public: + value() = default; + + template < typename T > + value(T&& value) + : raw_{std::forward(value)} {} + + template < typename T > + auto cast() const { + return std::any_cast(raw_); + } + + template < typename T > + auto try_cast() noexcept { + return std::any_cast(&raw_); + } + + template < typename T > + auto try_cast() const noexcept { + return std::any_cast(&raw_); + } + private: + std::any raw_; + }; +} diff --git a/headers/meta.hpp/meta_variable.hpp b/headers/meta.hpp/meta_variable.hpp new file mode 100644 index 0000000..6698570 --- /dev/null +++ b/headers/meta.hpp/meta_variable.hpp @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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" + +namespace meta_hpp::variable_detail +{ + template < typename Variable > + struct variable_traits; + + template < typename T > + struct variable_traits { + static constexpr bool is_const = false; + using value_type = T; + }; + + template < typename T > + struct variable_traits { + static constexpr bool is_const = true; + using value_type = T; + }; + + template < auto Variable > + value getter() { + using vt = variable_traits; + using value_type = typename vt::value_type; + + value_type typed_value = *Variable; + + return value{std::move(typed_value)}; + } + + template < auto Variable > + void setter(value value) { + using vt = variable_traits; + using value_type = typename vt::value_type; + + if constexpr ( !vt::is_const ) { + *Variable = value.cast(); + } else { + throw std::logic_error("an attempt to change a constant variable"); + } + } +} + +namespace meta_hpp +{ + class variable_info { + public: + variable_info() = delete; + + variable_info(variable_info&&) = default; + variable_info(const variable_info&) = default; + + variable_info& operator=(variable_info&&) = default; + variable_info& operator=(const variable_info&) = default; + + variable_info(std::string id) + : id_(std::move(id)) {} + + const std::string& id() const noexcept { + return id_; + } + public: + value get() const { + return getter_(); + } + + template < typename Value > + void set(Value&& value) const { + return setter_(std::forward(value)); + } + private: + friend class class_info; + friend class namespace_info; + template < typename Class > friend class class_; + friend class namespace_; + template < auto Variable > friend class variable_; + private: + void merge_with_(const variable_info& other) { + (void)other; + } + private: + std::string id_; + value(*getter_)(); + void(*setter_)(value); + }; +} + +namespace meta_hpp +{ + template < auto Variable > + class variable_ { + public: + static_assert(std::is_pointer_v); + + explicit variable_(std::string id) + : info_(std::move(id)) { + info_.getter_ = &variable_detail::getter; + info_.setter_ = &variable_detail::setter; + } + + const variable_info& info() const noexcept { + return info_; + } + private: + variable_info info_; + }; +} diff --git a/untests/meta_class_tests.cpp b/untests/meta_class_tests.cpp new file mode 100644 index 0000000..d399e53 --- /dev/null +++ b/untests/meta_class_tests.cpp @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class clazz { + public: + class clazz2 { + }; + + int field{1}; + const int cfield{2}; + + static int func(int a) { return a; } + + int method(int a) { return a; } + int cmethod(int a) const { return a; } + + static int variable; + static const int cvariable; + }; + + int clazz::variable{1}; + const int clazz::cvariable{2}; +} + +TEST_CASE("meta/class") { + namespace meta = meta_hpp; + + meta::class_ class_{"clazz"}; + const meta::class_& cclass_ = class_; + const meta::class_info& clazz_info = cclass_.info(); + + CHECK_FALSE(clazz_info.get_class("clazz2")); + CHECK_FALSE(clazz_info.get_field("field")); + CHECK_FALSE(clazz_info.get_field("cfield")); + CHECK_FALSE(clazz_info.get_function("func")); + CHECK_FALSE(clazz_info.get_method("method")); + CHECK_FALSE(clazz_info.get_method("cmethod")); + CHECK_FALSE(clazz_info.get_variable("variable")); + CHECK_FALSE(clazz_info.get_variable("cvariable")); + + class_( + meta::class_("clazz2"), + meta::field_<&clazz::field>("field"), + meta::field_<&clazz::cfield>("cfield"), + meta::function_<&clazz::func>("func"), + meta::method_<&clazz::method>("method"), + meta::method_<&clazz::cmethod>("cmethod"), + meta::variable_<&clazz::variable>("variable"), + meta::variable_<&clazz::cvariable>("cvariable")); + + CHECK(clazz_info.get_class("clazz2")); + CHECK(clazz_info.get_field("field")); + CHECK(clazz_info.get_field("cfield")); + CHECK(clazz_info.get_function("func")); + CHECK(clazz_info.get_method("method")); + CHECK(clazz_info.get_method("cmethod")); + CHECK(clazz_info.get_variable("variable")); + CHECK(clazz_info.get_variable("cvariable")); + + { + meta::class_info clazz2_info = clazz_info.get_class("clazz2").value(); + CHECK(clazz2_info.id() == "clazz2"); + } + + { + meta::field_info field_info = clazz_info.get_field("field").value(); + CHECK(field_info.id() == "field"); + } + + { + meta::field_info cfield_info = clazz_info.get_field("cfield").value(); + CHECK(cfield_info.id() == "cfield"); + } + + { + meta::function_info function_info = clazz_info.get_function("func").value(); + CHECK(function_info.id() == "func"); + } + + { + meta::method_info method_info = clazz_info.get_method("method").value(); + CHECK(method_info.id() == "method"); + } + + { + meta::method_info cmethod_info = clazz_info.get_method("cmethod").value(); + CHECK(cmethod_info.id() == "cmethod"); + } + + { + meta::variable_info variable_info = clazz_info.get_variable("variable").value(); + CHECK(variable_info.id() == "variable"); + } + + { + meta::variable_info cvariable_info = clazz_info.get_variable("cvariable").value(); + CHECK(cvariable_info.id() == "cvariable"); + } +} diff --git a/untests/meta_examples.cpp b/untests/meta_examples.cpp new file mode 100644 index 0000000..4df1896 --- /dev/null +++ b/untests/meta_examples.cpp @@ -0,0 +1,142 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class ivec2 { + public: + int x{}; + int y{}; + + ivec2() = default; + explicit ivec2(int v): x{v}, y{v} {} + ivec2(int x, int y): x{x}, y{y} {} + + int dot(ivec2 other) const { + return x * other.x + y * other.y; + } + + int length2() const { + return dot(*this); + } + }; + + class ivec3 { + public: + int x{}; + int y{}; + int z{}; + + ivec3() = default; + explicit ivec3(int v): x{v}, y{v}, z{v} {} + ivec3(int x, int y, int z): x{x}, y{y}, z{z} {} + + int dot(ivec3 other) const { + return x * other.x + y * other.y + z * other.z; + } + + int length2() const { + return dot(*this); + } + }; + + ivec2 add(ivec2 l, ivec2 r) { + return {l.x + r.x, l.y + r.y}; + } + + ivec3 add(ivec3 l, ivec3 r) { + return {l.x + r.x, l.y + r.y, l.z + r.z}; + } +} + +TEST_CASE("meta/examples/ivec2") { + SUBCASE("ctors") { + CHECK(ivec2{}.x == 0); + CHECK(ivec2{}.y == 0); + + CHECK(ivec2{1}.x == 1); + CHECK(ivec2{1}.y == 1); + + CHECK(ivec2{2,3}.x == 2); + CHECK(ivec2{2,3}.y == 3); + } +} + +TEST_CASE("meta/examples/ivec3") { + SUBCASE("ctors") { + CHECK(ivec3{}.x == 0); + CHECK(ivec3{}.y == 0); + CHECK(ivec3{}.z == 0); + + CHECK(ivec3{1}.x == 1); + CHECK(ivec3{1}.y == 1); + CHECK(ivec3{1}.z == 1); + + CHECK(ivec3{2,3,4}.x == 2); + CHECK(ivec3{2,3,4}.y == 3); + CHECK(ivec3{2,3,4}.z == 4); + } +} + +TEST_CASE("meta/examples/simple") { + namespace meta = meta_hpp; + + auto registry = meta::registry{}( + meta::namespace_("vmath")( + meta::class_("ivec2")( + meta::field_<&ivec2::x>("x"), + meta::field_<&ivec2::y>("y"), + meta::method_<&ivec2::dot>("dot"), + meta::method_<&ivec2::length2>("length2") + ), + meta::class_("ivec3")( + meta::field_<&ivec3::x>("x"), + meta::field_<&ivec3::y>("y"), + meta::field_<&ivec3::z>("z"), + meta::method_<&ivec3::dot>("dot"), + meta::method_<&ivec3::length2>("length2") + ), + meta::function_(&add)>("iadd2"), + meta::function_(&add)>("iadd3") + ) + ); + + meta::namespace_info vmath_info = registry.get_namespace("vmath").value(); + + meta::class_info ivec2_info = vmath_info.get_class("ivec2").value(); + meta::field_info ivec2_x_info = ivec2_info.get_field("x").value(); + meta::field_info ivec2_y_info = ivec2_info.get_field("y").value(); + + meta::method_info ivec2_dot_info = ivec2_info.get_method("dot").value(); + meta::method_info ivec2_length2_info = ivec2_info.get_method("length2").value(); + + meta::function_info iadd2_info = vmath_info.get_function("iadd2").value(); + meta::function_info iadd3_info = vmath_info.get_function("iadd3").value(); + + { + ivec2 v2{1,2}; + CHECK(ivec2_x_info.get(&v2).cast() == 1); + CHECK(ivec2_y_info.get(&v2).cast() == 2); + CHECK(ivec2_dot_info.invoke(&v2, v2).cast() == 5); + CHECK(ivec2_length2_info.invoke(&v2).cast() == 5); + } + + { + ivec2 v = iadd2_info.invoke(ivec2{1,2}, ivec2{3,4}).cast(); + CHECK(v.x == 4); + CHECK(v.y == 6); + } + + { + ivec3 v = iadd3_info.invoke(ivec3{1,2,3}, ivec3{3,4,5}).cast(); + CHECK(v.x == 4); + CHECK(v.y == 6); + CHECK(v.z == 8); + } +} diff --git a/untests/meta_field_tests.cpp b/untests/meta_field_tests.cpp new file mode 100644 index 0000000..05adcf6 --- /dev/null +++ b/untests/meta_field_tests.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class clazz { + public: + int field{1}; + const int cfield{2}; + }; +} + +TEST_CASE("meta/field") { + namespace meta = meta_hpp; + + meta::field_<&clazz::field> field_{"field"}; + meta::field_<&clazz::cfield> cfield_{"cfield"}; + + const meta::field_info& field_info = field_.info(); + const meta::field_info& cfield_info = cfield_.info(); + + { + clazz instance; + + CHECK(instance.field == 1); + CHECK(field_info.get(&instance).cast() == 1); + CHECK(field_info.get(&std::as_const(instance)).cast() == 1); + + CHECK_NOTHROW(field_info.set(&instance, 3)); + + CHECK(instance.field == 3); + CHECK(field_info.get(&instance).cast() == 3); + CHECK(field_info.get(&std::as_const(instance)).cast() == 3); + } + + { + clazz instance; + + CHECK(instance.cfield == 2); + CHECK(cfield_info.get(&instance).cast() == 2); + CHECK(cfield_info.get(&std::as_const(instance)).cast() == 2); + + CHECK_THROWS_AS(cfield_info.set(&instance, 4), std::logic_error); + + CHECK(instance.cfield == 2); + CHECK(cfield_info.get(&instance).cast() == 2); + CHECK(cfield_info.get(&std::as_const(instance)).cast() == 2); + } + + { + clazz instance; + + instance.field = 5; + CHECK(field_info.get(&instance).cast() == 5); + CHECK(field_info.get(&std::as_const(instance)).cast() == 5); + } +} diff --git a/untests/meta_function_tests.cpp b/untests/meta_function_tests.cpp new file mode 100644 index 0000000..d599fa1 --- /dev/null +++ b/untests/meta_function_tests.cpp @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + void void_f_void() noexcept {} + void void_f_int(int a) { (void)a; } + void void_f_int2(int a, int b) { (void)a; (void)b; } + + int int_f_void() noexcept { return 1; } + int int_f_int(int a) { return a; } + int int_f_int2(int a, int b) { return a + b; } +} + +TEST_CASE("meta/function") { + namespace meta = meta_hpp; + + meta::function_<&void_f_void> void_f_void_function_("void_f_void"); + meta::function_<&void_f_int> void_f_int_function_("void_f_int"); + meta::function_<&void_f_int2> void_f_int2_function_("void_f_int2"); + + meta::function_<&int_f_void> int_f_void_function_("int_f_void"); + meta::function_<&int_f_int> int_f_int_function_("int_f_int"); + meta::function_<&int_f_int2> int_f_int2_function_("int_f_int2"); + + const meta::function_info& void_f_void_info = void_f_void_function_.info(); + const meta::function_info& void_f_int_info = void_f_int_function_.info(); + const meta::function_info& void_f_int2_info = void_f_int2_function_.info(); + + const meta::function_info& int_f_void_info = int_f_void_function_.info(); + const meta::function_info& int_f_int_info = int_f_int_function_.info(); + const meta::function_info& int_f_int2_info = int_f_int2_function_.info(); + + SUBCASE("void_return") { + CHECK_NOTHROW(void_f_void_info.invoke()); + CHECK_THROWS_AS(void_f_void_info.invoke(1), std::logic_error); + + CHECK_THROWS_AS(void_f_int_info.invoke(), std::logic_error); + CHECK_NOTHROW(void_f_int_info.invoke(1)); + CHECK_THROWS_AS(void_f_int_info.invoke(1.f), std::logic_error); + CHECK_THROWS_AS(void_f_int_info.invoke(1, 2), std::logic_error); + + CHECK_THROWS_AS(void_f_int2_info.invoke(), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(1), std::logic_error); + CHECK_NOTHROW(void_f_int2_info.invoke(1, 2)); + CHECK_THROWS_AS(void_f_int2_info.invoke(1.f, 2), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(1, 2.f), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(1, 2, 3), std::logic_error); + } + + SUBCASE("int_return") { + CHECK(int_f_void_info.invoke().cast() == 1); + CHECK_THROWS_AS(int_f_void_info.invoke(1), std::logic_error); + + CHECK_THROWS_AS(int_f_int_info.invoke(), std::logic_error); + CHECK(int_f_int_info.invoke(1).cast() == 1); + CHECK_THROWS_AS(int_f_int_info.invoke(1.f), std::logic_error); + CHECK_THROWS_AS(int_f_int_info.invoke(1, 2), std::logic_error); + + CHECK_THROWS_AS(int_f_int2_info.invoke(), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(1), std::logic_error); + CHECK(int_f_int2_info.invoke(1, 2).cast() == 3); + CHECK_THROWS_AS(int_f_int2_info.invoke(1.f, 2), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(1, 2.f), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(1, 2, 3), std::logic_error); + } +} diff --git a/untests/meta_method_tests.cpp b/untests/meta_method_tests.cpp new file mode 100644 index 0000000..19011e2 --- /dev/null +++ b/untests/meta_method_tests.cpp @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class clazz { + public: + void void_f_void() noexcept {} + void void_f_int(int a) { (void)a; } + void void_f_int2(int a, int b) { (void)a; (void)b; } + + int int_f_void() noexcept { return 1; } + int int_f_int(int a) { return a; } + int int_f_int2(int a, int b) { return a + b; } + + void const_void_f_void() const noexcept {} + void const_void_f_int(int a) const { (void)a; } + void const_void_f_int2(int a, int b) const { (void)a; (void)b; } + + int const_int_f_void() const noexcept { return 1; } + int const_int_f_int(int a) const { return a; } + int const_int_f_int2(int a, int b) const { return a + b; } + }; +} + +TEST_CASE("meta/non_const_method") { + namespace meta = meta_hpp; + + meta::method_<&clazz::void_f_void> void_f_void_method_("void_f_void"); + meta::method_<&clazz::void_f_int> void_f_int_method_("void_f_int"); + meta::method_<&clazz::void_f_int2> void_f_int2_method_("void_f_int2"); + + meta::method_<&clazz::int_f_void> int_f_void_method_("int_f_void"); + meta::method_<&clazz::int_f_int> int_f_int_method_("int_f_int"); + meta::method_<&clazz::int_f_int2> int_f_int2_method_("int_f_int2"); + + const meta::method_info& void_f_void_info = void_f_void_method_.info(); + const meta::method_info& void_f_int_info = void_f_int_method_.info(); + const meta::method_info& void_f_int2_info = void_f_int2_method_.info(); + + const meta::method_info& int_f_void_info = int_f_void_method_.info(); + const meta::method_info& int_f_int_info = int_f_int_method_.info(); + const meta::method_info& int_f_int2_info = int_f_int2_method_.info(); + + SUBCASE("void_return") { + clazz instance; + + CHECK_NOTHROW(void_f_void_info.invoke(&instance)); + CHECK_THROWS_AS(void_f_void_info.invoke(&instance, 1), std::logic_error); + + CHECK_THROWS_AS(void_f_int_info.invoke(&instance), std::logic_error); + CHECK_NOTHROW(void_f_int_info.invoke(&instance, 1)); + CHECK_THROWS_AS(void_f_int_info.invoke(&instance, 1.f), std::logic_error); + CHECK_THROWS_AS(void_f_int_info.invoke(&instance, 1, 2), std::logic_error); + + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1), std::logic_error); + CHECK_NOTHROW(void_f_int2_info.invoke(&instance, 1, 2)); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1.f, 2), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1, 2.f), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1, 2, 3), std::logic_error); + + const clazz& cinstance = instance; + CHECK_THROWS_AS(void_f_void_info.invoke(&cinstance), std::logic_error); + CHECK_THROWS_AS(void_f_int_info.invoke(&cinstance, 1), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&cinstance, 1, 2), std::logic_error); + } + + SUBCASE("int_return") { + clazz instance; + + CHECK(int_f_void_info.invoke(&instance).cast() == 1); + CHECK_THROWS_AS(int_f_void_info.invoke(&instance, 1), std::logic_error); + + CHECK_THROWS_AS(int_f_int_info.invoke(&instance), std::logic_error); + CHECK(int_f_int_info.invoke(&instance, 1).cast() == 1); + CHECK_THROWS_AS(int_f_int_info.invoke(&instance, 1.f), std::logic_error); + CHECK_THROWS_AS(int_f_int_info.invoke(&instance, 1, 2), std::logic_error); + + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1), std::logic_error); + CHECK(int_f_int2_info.invoke(&instance, 1, 2).cast() == 3); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1.f, 2), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1, 2.f), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1, 2, 3), std::logic_error); + + const clazz& cinstance = instance; + CHECK_THROWS_AS(int_f_void_info.invoke(&cinstance), std::logic_error); + CHECK_THROWS_AS(int_f_int_info.invoke(&cinstance, 1), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&cinstance, 1, 2), std::logic_error); + } +} + +TEST_CASE("meta/const_method") { + namespace meta = meta_hpp; + + meta::method_<&clazz::const_void_f_void> void_f_void_method_("void_f_void"); + meta::method_<&clazz::const_void_f_int> void_f_int_method_("void_f_int"); + meta::method_<&clazz::const_void_f_int2> void_f_int2_method_("void_f_int2"); + + meta::method_<&clazz::const_int_f_void> int_f_void_method_("int_f_void"); + meta::method_<&clazz::const_int_f_int> int_f_int_method_("int_f_int"); + meta::method_<&clazz::const_int_f_int2> int_f_int2_method_("int_f_int2"); + + const meta::method_info& void_f_void_info = void_f_void_method_.info(); + const meta::method_info& void_f_int_info = void_f_int_method_.info(); + const meta::method_info& void_f_int2_info = void_f_int2_method_.info(); + + const meta::method_info& int_f_void_info = int_f_void_method_.info(); + const meta::method_info& int_f_int_info = int_f_int_method_.info(); + const meta::method_info& int_f_int2_info = int_f_int2_method_.info(); + + SUBCASE("void_return") { + clazz instance; + + CHECK_NOTHROW(void_f_void_info.invoke(&instance)); + CHECK_THROWS_AS(void_f_void_info.invoke(&instance, 1), std::logic_error); + + CHECK_THROWS_AS(void_f_int_info.invoke(&instance), std::logic_error); + CHECK_NOTHROW(void_f_int_info.invoke(&instance, 1)); + CHECK_THROWS_AS(void_f_int_info.invoke(&instance, 1.f), std::logic_error); + CHECK_THROWS_AS(void_f_int_info.invoke(&instance, 1, 2), std::logic_error); + + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1), std::logic_error); + CHECK_NOTHROW(void_f_int2_info.invoke(&instance, 1, 2)); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1.f, 2), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1, 2.f), std::logic_error); + CHECK_THROWS_AS(void_f_int2_info.invoke(&instance, 1, 2, 3), std::logic_error); + + const clazz& cinstance = instance; + CHECK_NOTHROW(void_f_void_info.invoke(&cinstance)); + CHECK_NOTHROW(void_f_int_info.invoke(&cinstance, 1)); + CHECK_NOTHROW(void_f_int2_info.invoke(&cinstance, 1, 2)); + } + + SUBCASE("int_return") { + clazz instance; + + CHECK(int_f_void_info.invoke(&instance).cast() == 1); + CHECK_THROWS_AS(int_f_void_info.invoke(&instance, 1), std::logic_error); + + CHECK_THROWS_AS(int_f_int_info.invoke(&instance), std::logic_error); + CHECK(int_f_int_info.invoke(&instance, 1).cast() == 1); + CHECK_THROWS_AS(int_f_int_info.invoke(&instance, 1.f), std::logic_error); + CHECK_THROWS_AS(int_f_int_info.invoke(&instance, 1, 2), std::logic_error); + + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1), std::logic_error); + CHECK(int_f_int2_info.invoke(&instance, 1, 2).cast() == 3); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1.f, 2), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1, 2.f), std::logic_error); + CHECK_THROWS_AS(int_f_int2_info.invoke(&instance, 1, 2, 3), std::logic_error); + + const clazz& cinstance = instance; + CHECK(int_f_void_info.invoke(&cinstance).cast() == 1); + CHECK(int_f_int_info.invoke(&cinstance, 1).cast() == 1); + CHECK(int_f_int2_info.invoke(&cinstance, 1, 2).cast() == 3); + } +} diff --git a/untests/meta_namespace_tests.cpp b/untests/meta_namespace_tests.cpp new file mode 100644 index 0000000..146d71c --- /dev/null +++ b/untests/meta_namespace_tests.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class clazz {}; + int func(int a) { return a; } + int variable{1}; + const int cvariable{2}; +} + +TEST_CASE("meta/namespace") { + namespace meta = meta_hpp; + + meta::namespace_ namespace_{"ns"}; + const meta::namespace_& cnamespace_ = namespace_; + const meta::namespace_info& ns_info = cnamespace_.info(); + + CHECK_FALSE(ns_info.get_class("clazz")); + CHECK_FALSE(ns_info.get_function("func")); + CHECK_FALSE(ns_info.get_namespace("ns")); + CHECK_FALSE(ns_info.get_variable("variable")); + CHECK_FALSE(ns_info.get_variable("cvariable")); + + namespace_( + meta::class_("clazz"), + meta::function_<&func>("func"), + meta::namespace_("ns2"), + meta::variable_<&variable>("variable"), + meta::variable_<&cvariable>("cvariable")); + + CHECK(ns_info.get_class("clazz")); + CHECK(ns_info.get_function("func")); + CHECK(ns_info.get_namespace("ns2")); + CHECK(ns_info.get_variable("variable")); + CHECK(ns_info.get_variable("cvariable")); + + { + meta::class_info clazz_info = ns_info.get_class("clazz").value(); + CHECK(clazz_info.id() == "clazz"); + } + + { + meta::function_info clazz_info = ns_info.get_function("func").value(); + CHECK(clazz_info.id() == "func"); + } + + { + meta::namespace_info namespace_info = ns_info.get_namespace("ns2").value(); + CHECK(namespace_info.id() == "ns2"); + } + + { + meta::variable_info variable_info = ns_info.get_variable("variable").value(); + CHECK(variable_info.id() == "variable"); + } + + { + meta::variable_info cvariable_info = ns_info.get_variable("cvariable").value(); + CHECK(cvariable_info.id() == "cvariable"); + } +} diff --git a/untests/meta_registry_tests.cpp b/untests/meta_registry_tests.cpp new file mode 100644 index 0000000..e62fc43 --- /dev/null +++ b/untests/meta_registry_tests.cpp @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + class clazz {}; + int func(int a) { return a; } + int variable{1}; + const int cvariable{2}; +} + +TEST_CASE("meta/registry") { + namespace meta = meta_hpp; + + meta::registry registry; + const meta::registry& cregistry = registry; + + CHECK_FALSE(cregistry.get_class("clazz")); + CHECK_FALSE(cregistry.get_function("func")); + CHECK_FALSE(cregistry.get_namespace("ns")); + CHECK_FALSE(cregistry.get_variable("variable")); + CHECK_FALSE(cregistry.get_variable("cvariable")); + + registry( + meta::class_("clazz"), + meta::function_<&func>("func"), + meta::namespace_("ns"), + meta::variable_<&variable>("variable"), + meta::variable_<&cvariable>("cvariable")); + + CHECK(cregistry.get_class("clazz")); + CHECK(cregistry.get_function("func")); + CHECK(cregistry.get_namespace("ns")); + CHECK(cregistry.get_variable("variable")); + CHECK(cregistry.get_variable("cvariable")); + + { + meta::class_info clazz_info = cregistry.get_class("clazz").value(); + CHECK(clazz_info.id() == "clazz"); + } + + { + meta::function_info clazz_info = cregistry.get_function("func").value(); + CHECK(clazz_info.id() == "func"); + } + + { + meta::namespace_info namespace_info = cregistry.get_namespace("ns").value(); + CHECK(namespace_info.id() == "ns"); + } + + { + meta::variable_info variable_info = cregistry.get_variable("variable").value(); + CHECK(variable_info.id() == "variable"); + } + + { + meta::variable_info cvariable_info = cregistry.get_variable("cvariable").value(); + CHECK(cvariable_info.id() == "cvariable"); + } +} diff --git a/untests/meta_tests.cpp b/untests/meta_tests.cpp deleted file mode 100644 index 8f5b33a..0000000 --- a/untests/meta_tests.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/******************************************************************************* - * 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 "meta_tests.hpp" - -namespace -{ - using namespace meta_hpp; - using namespace meta_tests; -} - -TEST_CASE("meta") { -} diff --git a/untests/meta_tests.hpp b/untests/meta_value_tests.cpp similarity index 79% rename from untests/meta_tests.hpp rename to untests/meta_value_tests.cpp index 2b0e426..f01d424 100644 --- a/untests/meta_tests.hpp +++ b/untests/meta_value_tests.cpp @@ -4,10 +4,13 @@ * Copyright (C) 2021, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ -#include +#include #include "doctest/doctest.hpp" -namespace meta_tests +namespace { - using namespace meta_hpp; +} + +TEST_CASE("meta/value") { + namespace meta = meta_hpp; } diff --git a/untests/meta_variable_tests.cpp b/untests/meta_variable_tests.cpp new file mode 100644 index 0000000..e70a55b --- /dev/null +++ b/untests/meta_variable_tests.cpp @@ -0,0 +1,93 @@ +/******************************************************************************* + * 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 +#include "doctest/doctest.hpp" + +namespace +{ + int variable{1}; + const int cvariable{2}; + + class clazz { + public: + static int variable; + static const int cvariable; + }; + + int clazz::variable{1}; + const int clazz::cvariable{2}; +} + +TEST_CASE("meta/variable") { + namespace meta = meta_hpp; + + SUBCASE("in_namespace") { + meta::variable_<&variable> variable_{"variable"}; + meta::variable_<&cvariable> cvariable_{"cvariable"}; + + const meta::variable_info& variable_info = variable_.info(); + const meta::variable_info& cvariable_info = cvariable_.info(); + + { + CHECK(variable == 1); + CHECK(variable_info.get().cast() == 1); + + CHECK_NOTHROW(variable_info.set(3)); + + CHECK(variable == 3); + CHECK(variable_info.get().cast() == 3); + } + + { + CHECK(cvariable == 2); + CHECK(cvariable_info.get().cast() == 2); + + CHECK_THROWS_AS(cvariable_info.set(4), std::logic_error); + + CHECK(cvariable == 2); + CHECK(cvariable_info.get().cast() == 2); + } + + { + variable = 5; + CHECK(variable_info.get().cast() == 5); + } + } + + SUBCASE("in_class") { + meta::variable_<&clazz::variable> variable_{"variable"}; + meta::variable_<&clazz::cvariable> cvariable_{"cvariable"}; + + const meta::variable_info& variable_info = variable_.info(); + const meta::variable_info& cvariable_info = cvariable_.info(); + + { + CHECK(clazz::variable == 1); + CHECK(variable_info.get().cast() == 1); + + CHECK_NOTHROW(variable_info.set(3)); + + CHECK(clazz::variable == 3); + CHECK(variable_info.get().cast() == 3); + } + + { + CHECK(clazz::cvariable == 2); + CHECK(cvariable_info.get().cast() == 2); + + CHECK_THROWS_AS(cvariable_info.set(4), std::logic_error); + + CHECK(clazz::cvariable == 2); + CHECK(cvariable_info.get().cast() == 2); + } + + { + clazz::variable = 5; + CHECK(variable_info.get().cast() == 5); + } + } +}