diff --git a/headers/meta.hpp/meta_registry.hpp b/headers/meta.hpp/meta_registry.hpp index feb24ae..149189a 100644 --- a/headers/meta.hpp/meta_registry.hpp +++ b/headers/meta.hpp/meta_registry.hpp @@ -21,88 +21,152 @@ namespace meta_hpp { class registry { public: + template < typename T > + std::optional resolve() const { + return detail::find_opt(family_to_types_, get_family_id()); + } + template < auto T > std::optional resolve() const { - const family_id fid = get_family_id(); - return detail::find_opt(family_to_types_, fid); + return resolve(); } template < typename T > - std::optional resolve() const { - const family_id fid = get_family_id(); - return detail::find_opt(family_to_types_, fid); + std::optional resolve(T&&) const { + return resolve>(); } - template < typename T > - std::optional resolve(const T&) const { - const family_id fid = get_family_id(); - return detail::find_opt(family_to_types_, fid); + std::optional get_class_by_name(std::string_view name) const { + return detail::find_opt(name_to_classes_, name); + } + + std::optional get_field_by_name(std::string_view name) const { + return detail::find_opt(name_to_fields_, name); + } + + std::optional get_function_by_name(std::string_view name) const { + return detail::find_opt(name_to_functions_, name); + } + + std::optional get_method_by_name(std::string_view name) const { + return detail::find_opt(name_to_methods_, name); + } + + std::optional get_namespace_by_name(std::string_view name) const { + return detail::find_opt(name_to_namespaces_, name); + } + + std::optional get_variable_by_name(std::string_view name) const { + return detail::find_opt(name_to_variables_, name); } template < typename... Internals > registry& operator()(Internals&&...internals) { - (add_(std::forward(internals)), ...); + (add_(std::string{}, std::forward(internals)), ...); return *this; } private: - void add_(const class_info& info) { - family_to_types_.emplace(info.fid(), info); + void add_(const std::string& prefix, const class_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); - info.each_class([this](const class_info& internal){ - add_(internal); - }); + detail::merge_with(name_to_classes_, name, info, &class_info::merge); + detail::merge_with(family_to_types_, info.fid(), info, &type::merge); - info.each_field([this](const field_info& internal){ - add_(internal); - }); - - info.each_function([this](const function_info& internal){ - add_(internal); - }); - - info.each_method([this](const method_info& internal){ - add_(internal); - }); - - info.each_variable([this](const variable_info& internal){ - add_(internal); + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } }); } - void add_(const field_info& info) { - family_to_types_.emplace(info.fid(), info); - } + void add_(const std::string& prefix, const field_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); - void add_(const function_info& info) { - family_to_types_.emplace(info.fid(), info); - } + detail::merge_with(name_to_fields_, name, info, &field_info::merge); + detail::merge_with(family_to_types_, info.fid(), info, &type::merge); - void add_(const method_info& info) { - family_to_types_.emplace(info.fid(), info); - } - - void add_(const namespace_info& info) { - info.each_class([this](const class_info& internal){ - add_(internal); - }); - - info.each_function([this](const function_info& internal){ - add_(internal); - }); - - info.each_namespace([this](const namespace_info& internal){ - add_(internal); - }); - - info.each_variable([this](const variable_info& internal){ - add_(internal); + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } }); } - void add_(const variable_info& info) { - family_to_types_.emplace(info.fid(), info); + void add_(const std::string& prefix, const function_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); + + detail::merge_with(name_to_functions_, name, info, &function_info::merge); + detail::merge_with(family_to_types_, info.fid(), info, &type::merge); + + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } + }); + } + + void add_(const std::string& prefix, const method_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); + + detail::merge_with(name_to_methods_, name, info, &method_info::merge); + detail::merge_with(family_to_types_, info.fid(), info, &type::merge); + + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } + }); + } + + void add_(const std::string& prefix, const namespace_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); + + detail::merge_with(name_to_namespaces_, name, info, &namespace_info::merge); + + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } + }); + } + + void add_(const std::string& prefix, const variable_info& info) { + const std::string name = prefix.empty() + ? info.id() + : prefix + "::" + info.id(); + + detail::merge_with(name_to_variables_, name, info, &variable_info::merge); + detail::merge_with(family_to_types_, info.fid(), info, &type::merge); + + info.visit(overloaded { + [](const data_info&) {}, + [this, &name](const auto& internal){ + add_(name, internal); + } + }); } private: std::map> family_to_types_; + std::map> name_to_classes_; + std::map> name_to_fields_; + std::map> name_to_functions_; + std::map> name_to_methods_; + std::map> name_to_namespaces_; + std::map> name_to_variables_; }; } diff --git a/headers/meta.hpp/meta_type.hpp b/headers/meta.hpp/meta_type.hpp index c2a5ab1..01033e9 100644 --- a/headers/meta.hpp/meta_type.hpp +++ b/headers/meta.hpp/meta_type.hpp @@ -31,19 +31,83 @@ namespace meta_hpp type(Info&& info) : info_{std::forward(info)} {} - bool is_class() const noexcept { return std::holds_alternative(info_); } - bool is_field() const noexcept { return std::holds_alternative(info_); } - bool is_function() const noexcept { return std::holds_alternative(info_); } - bool is_method() const noexcept { return std::holds_alternative(info_); } - bool is_namespace() const noexcept { return std::holds_alternative(info_); } - bool is_variable() const noexcept { return std::holds_alternative(info_); } + bool is_class() const noexcept { + return std::holds_alternative(info_); + } - const class_info& get_class_info() const { return std::get(info_); } - const field_info& get_field_info() const { return std::get(info_); } - const function_info& get_function_info() const { return std::get(info_); } - const method_info& get_method_info() const { return std::get(info_); } - const namespace_info& get_namespace_info() const { return std::get(info_); } - const variable_info& get_variable_info() const { return std::get(info_); } + bool is_field() const noexcept { + return std::holds_alternative(info_); + } + + bool is_function() const noexcept { + return std::holds_alternative(info_); + } + + bool is_method() const noexcept { + return std::holds_alternative(info_); + } + + bool is_namespace() const noexcept { + return std::holds_alternative(info_); + } + + bool is_variable() const noexcept { + return std::holds_alternative(info_); + } + + std::optional get_class() const { + if ( const class_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + std::optional get_field() const { + if ( const field_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + std::optional get_function() const { + if ( const function_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + std::optional get_method() const { + if ( const method_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + std::optional get_namespace() const { + if ( const namespace_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + std::optional get_variable() const { + if ( const variable_info* info = std::get_if(&info_); info ) { + return *info; + } + return std::nullopt; + } + + void merge(const type& other) { + if ( info_.index() != other.info_.index() ) { + throw std::logic_error("type::merge failed"); + } + std::visit(overloaded { + [&other](auto& info){ + using info_type = std::remove_reference_t; + info.merge(std::get(other.info_)); + } + }, info_); + } private: std::variant< class_info, diff --git a/untests/meta_examples.cpp b/untests/meta_examples.cpp index 8c97308..1c377ef 100644 --- a/untests/meta_examples.cpp +++ b/untests/meta_examples.cpp @@ -173,7 +173,7 @@ TEST_CASE("meta/examples/advanced") { ivec3 v3{1,2,3}; type v3_type = db.resolve(v3).value(); - class_info v3_info = v3_type.get_class_info(); + class_info v3_info = v3_type.get_class().value(); std::cout << "class " << v3_info.id() << std::endl; diff --git a/untests/meta_registry_tests.cpp b/untests/meta_registry_tests.cpp index 7dce6b8..1ab1574 100644 --- a/untests/meta_registry_tests.cpp +++ b/untests/meta_registry_tests.cpp @@ -68,13 +68,22 @@ TEST_CASE("meta/registry") { ) ); + SUBCASE("get_xxx_by_name") { + CHECK(registry.get_class_by_name("vmath::ivec2")); + CHECK(registry.get_field_by_name("vmath::ivec2::x")); + CHECK(registry.get_function_by_name("vmath::iadd2")); + CHECK(registry.get_method_by_name("vmath::ivec2::dot")); + CHECK(registry.get_namespace_by_name("vmath")); + CHECK(registry.get_variable_by_name("vmath::ivec2::zero")); + } + SUBCASE("class_template") { CHECK(registry.resolve()); const meta::type ivec2_type = registry.resolve().value(); CHECK(ivec2_type.is_class()); - const meta::class_info& ivec2_info = ivec2_type.get_class_info(); + const meta::class_info ivec2_info = ivec2_type.get_class().value(); CHECK(ivec2_info.id() == "ivec2"); } @@ -85,7 +94,7 @@ TEST_CASE("meta/registry") { const meta::type ivec3_type = registry.resolve(v3).value(); CHECK(ivec3_type.is_class()); - const meta::class_info& ivec3_info = ivec3_type.get_class_info(); + const meta::class_info ivec3_info = ivec3_type.get_class().value(); CHECK(ivec3_info.id() == "ivec3"); } @@ -95,7 +104,7 @@ TEST_CASE("meta/registry") { const meta::type ivec2_x_type = registry.resolve<&ivec2::x>().value(); CHECK(ivec2_x_type.is_field()); - const meta::field_info& ivec2_x_info = ivec2_x_type.get_field_info(); + const meta::field_info ivec2_x_info = ivec2_x_type.get_field().value(); CHECK(ivec2_x_info.id() == "x"); } @@ -105,7 +114,7 @@ TEST_CASE("meta/registry") { const meta::type ivec3_x_type = registry.resolve<&ivec3::x>().value(); CHECK(ivec3_x_type.is_field()); - const meta::field_info& ivec3_x_info = ivec3_x_type.get_field_info(); + const meta::field_info ivec3_x_info = ivec3_x_type.get_field().value(); CHECK(ivec3_x_info.id() == "x"); } @@ -115,7 +124,7 @@ TEST_CASE("meta/registry") { const meta::type iadd2_type = registry.resolve<&iadd2>().value(); CHECK(iadd2_type.is_function()); - const meta::function_info& iadd2_info = iadd2_type.get_function_info(); + const meta::function_info iadd2_info = iadd2_type.get_function().value(); CHECK(iadd2_info.id() == "iadd2"); } @@ -125,7 +134,7 @@ TEST_CASE("meta/registry") { const meta::type iadd3_type = registry.resolve<&iadd3>().value(); CHECK(iadd3_type.is_function()); - const meta::function_info& iadd3_info = iadd3_type.get_function_info(); + const meta::function_info iadd3_info = iadd3_type.get_function().value(); CHECK(iadd3_info.id() == "iadd3"); } @@ -135,7 +144,7 @@ TEST_CASE("meta/registry") { const meta::type ivec2_dot_type = registry.resolve<&ivec2::dot>().value(); CHECK(ivec2_dot_type.is_method()); - const meta::method_info& ivec2_dot_info = ivec2_dot_type.get_method_info(); + const meta::method_info ivec2_dot_info = ivec2_dot_type.get_method().value(); CHECK(ivec2_dot_info.id() == "dot"); } @@ -145,7 +154,7 @@ TEST_CASE("meta/registry") { const meta::type ivec3_dot_type = registry.resolve<&ivec3::dot>().value(); CHECK(ivec3_dot_type.is_method()); - const meta::method_info& ivec3_dot_info = ivec3_dot_type.get_method_info(); + const meta::method_info ivec3_dot_info = ivec3_dot_type.get_method().value(); CHECK(ivec3_dot_info.id() == "dot"); } @@ -155,7 +164,7 @@ TEST_CASE("meta/registry") { const meta::type ivec2_zero_type = registry.resolve<&ivec2::zero>().value(); CHECK(ivec2_zero_type.is_variable()); - const meta::variable_info& ivec2_x_info = ivec2_zero_type.get_variable_info(); + const meta::variable_info ivec2_x_info = ivec2_zero_type.get_variable().value(); CHECK(ivec2_x_info.id() == "zero"); } @@ -165,7 +174,54 @@ TEST_CASE("meta/registry") { const meta::type ivec3_zero_type = registry.resolve<&ivec3::zero>().value(); CHECK(ivec3_zero_type.is_variable()); - const meta::variable_info& ivec3_x_info = ivec3_zero_type.get_variable_info(); + const meta::variable_info ivec3_x_info = ivec3_zero_type.get_variable().value(); CHECK(ivec3_x_info.id() == "zero"); } } + +TEST_CASE("meta/registry/merge") { + namespace meta = meta_hpp; + using namespace std::string_literals; + + auto registry = meta::registry{}; + + SUBCASE("class") { + registry( + meta::class_("ivec2")( + meta::field_<&ivec2::x>("x") + )); + + registry( + meta::class_("ivec2")( + meta::field_<&ivec2::y>("y") + )); + + CHECK(registry.resolve()); + CHECK(registry.resolve()->get_class()); + CHECK(registry.resolve()->get_class()->get_field("x")); + CHECK(registry.resolve()->get_class()->get_field("y")); + + CHECK(registry.resolve(ivec2{})); + CHECK(registry.resolve(ivec2{})->get_class()); + CHECK(registry.resolve(ivec2{})->get_class()->get_field("x")); + CHECK(registry.resolve(ivec2{})->get_class()->get_field("y")); + + CHECK(registry.get_class_by_name("ivec2")); + CHECK(registry.get_class_by_name("ivec2")->get_field("x")); + CHECK(registry.get_class_by_name("ivec2")->get_field("y")); + } + + SUBCASE("namespace") { + registry( + meta::namespace_("vmath")( + meta::class_("ivec2"))); + + registry( + meta::namespace_("vmath")( + meta::class_("ivec3"))); + + CHECK(registry.get_namespace_by_name("vmath")); + CHECK(registry.get_namespace_by_name("vmath")->get_class("ivec2")); + CHECK(registry.get_namespace_by_name("vmath")->get_class("ivec3")); + } +} diff --git a/untests/meta_type_tests.cpp b/untests/meta_type_tests.cpp index 919c577..4e94218 100644 --- a/untests/meta_type_tests.cpp +++ b/untests/meta_type_tests.cpp @@ -33,25 +33,44 @@ TEST_CASE("meta/type") { meta::type class_type = meta::class_("clazz"); CHECK(class_type.is_class()); - CHECK(class_type.get_class_info().id() == "clazz"); + CHECK(class_type.get_class()->id() == "clazz"); meta::type field_type = meta::field_<&clazz::field>("field"); CHECK(field_type.is_field()); - CHECK(field_type.get_field_info().id() == "field"); + CHECK(field_type.get_field()->id() == "field"); meta::type function_type = meta::function_<&clazz::function>("function"); CHECK(function_type.is_function()); - CHECK(function_type.get_function_info().id() == "function"); + CHECK(function_type.get_function()->id() == "function"); meta::type method_type = meta::method_<&clazz::method>("method"); CHECK(method_type.is_method()); - CHECK(method_type.get_method_info().id() == "method"); + CHECK(method_type.get_method()->id() == "method"); meta::type namespace_type = meta::namespace_("ns"); CHECK(namespace_type.is_namespace()); - CHECK(namespace_type.get_namespace_info().id() == "ns"); + CHECK(namespace_type.get_namespace()->id() == "ns"); meta::type variable_type = meta::variable_<&clazz::variable>("variable"); CHECK(variable_type.is_variable()); - CHECK(variable_type.get_variable_info().id() == "variable"); + CHECK(variable_type.get_variable()->id() == "variable"); +} + +TEST_CASE("meta/type/merge") { + namespace meta = meta_hpp; + using namespace std::string_literals; + + { + meta::type clazz_type = meta::class_("clazz")( + meta::field_<&clazz::field>("field") + ); + + clazz_type.merge(meta::class_("clazz")( + meta::function_<&clazz::function>("function") + )); + + REQUIRE(clazz_type.is_class()); + CHECK(clazz_type.get_class()->get_field("field")); + CHECK(clazz_type.get_class()->get_function("function")); + } }