merge types in registry

This commit is contained in:
BlackMATov
2021-07-04 22:59:58 +07:00
parent 699f6beb76
commit 7df45ed270
5 changed files with 288 additions and 85 deletions

View File

@@ -21,88 +21,152 @@ namespace meta_hpp
{ {
class registry { class registry {
public: public:
template < typename T >
std::optional<type> resolve() const {
return detail::find_opt(family_to_types_, get_family_id<T>());
}
template < auto T > template < auto T >
std::optional<type> resolve() const { std::optional<type> resolve() const {
const family_id fid = get_family_id<decltype(T)>(); return resolve<decltype(T)>();
return detail::find_opt(family_to_types_, fid);
} }
template < typename T > template < typename T >
std::optional<type> resolve() const { std::optional<type> resolve(T&&) const {
const family_id fid = get_family_id<T>(); return resolve<std::decay_t<T>>();
return detail::find_opt(family_to_types_, fid);
} }
template < typename T > std::optional<class_info> get_class_by_name(std::string_view name) const {
std::optional<type> resolve(const T&) const { return detail::find_opt(name_to_classes_, name);
const family_id fid = get_family_id<T>(); }
return detail::find_opt(family_to_types_, fid);
std::optional<field_info> get_field_by_name(std::string_view name) const {
return detail::find_opt(name_to_fields_, name);
}
std::optional<function_info> get_function_by_name(std::string_view name) const {
return detail::find_opt(name_to_functions_, name);
}
std::optional<method_info> get_method_by_name(std::string_view name) const {
return detail::find_opt(name_to_methods_, name);
}
std::optional<namespace_info> get_namespace_by_name(std::string_view name) const {
return detail::find_opt(name_to_namespaces_, name);
}
std::optional<variable_info> get_variable_by_name(std::string_view name) const {
return detail::find_opt(name_to_variables_, name);
} }
template < typename... Internals > template < typename... Internals >
registry& operator()(Internals&&...internals) { registry& operator()(Internals&&...internals) {
(add_(std::forward<Internals>(internals)), ...); (add_(std::string{}, std::forward<Internals>(internals)), ...);
return *this; return *this;
} }
private: private:
void add_(const class_info& info) { void add_(const std::string& prefix, const class_info& info) {
family_to_types_.emplace(info.fid(), info); const std::string name = prefix.empty()
? info.id()
: prefix + "::" + info.id();
info.each_class([this](const class_info& internal){ detail::merge_with(name_to_classes_, name, info, &class_info::merge);
add_(internal); detail::merge_with(family_to_types_, info.fid(), info, &type::merge);
});
info.each_field([this](const field_info& internal){ info.visit(overloaded {
add_(internal); [](const data_info&) {},
}); [this, &name](const auto& internal){
add_(name, 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);
}); });
} }
void add_(const field_info& info) { void add_(const std::string& prefix, const field_info& info) {
family_to_types_.emplace(info.fid(), info); const std::string name = prefix.empty()
} ? info.id()
: prefix + "::" + info.id();
void add_(const function_info& info) { detail::merge_with(name_to_fields_, name, info, &field_info::merge);
family_to_types_.emplace(info.fid(), info); detail::merge_with(family_to_types_, info.fid(), info, &type::merge);
}
void add_(const method_info& info) { info.visit(overloaded {
family_to_types_.emplace(info.fid(), info); [](const data_info&) {},
} [this, &name](const auto& internal){
add_(name, internal);
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);
}); });
} }
void add_(const variable_info& info) { void add_(const std::string& prefix, const function_info& info) {
family_to_types_.emplace(info.fid(), 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: private:
std::map<family_id, type, std::less<>> family_to_types_; std::map<family_id, type, std::less<>> family_to_types_;
std::map<std::string, class_info, std::less<>> name_to_classes_;
std::map<std::string, field_info, std::less<>> name_to_fields_;
std::map<std::string, function_info, std::less<>> name_to_functions_;
std::map<std::string, method_info, std::less<>> name_to_methods_;
std::map<std::string, namespace_info, std::less<>> name_to_namespaces_;
std::map<std::string, variable_info, std::less<>> name_to_variables_;
}; };
} }

View File

@@ -31,19 +31,83 @@ namespace meta_hpp
type(Info&& info) type(Info&& info)
: info_{std::forward<Info>(info)} {} : info_{std::forward<Info>(info)} {}
bool is_class() const noexcept { return std::holds_alternative<class_info>(info_); } bool is_class() const noexcept {
bool is_field() const noexcept { return std::holds_alternative<field_info>(info_); } return std::holds_alternative<class_info>(info_);
bool is_function() const noexcept { return std::holds_alternative<function_info>(info_); } }
bool is_method() const noexcept { return std::holds_alternative<method_info>(info_); }
bool is_namespace() const noexcept { return std::holds_alternative<namespace_info>(info_); }
bool is_variable() const noexcept { return std::holds_alternative<variable_info>(info_); }
const class_info& get_class_info() const { return std::get<class_info>(info_); } bool is_field() const noexcept {
const field_info& get_field_info() const { return std::get<field_info>(info_); } return std::holds_alternative<field_info>(info_);
const function_info& get_function_info() const { return std::get<function_info>(info_); } }
const method_info& get_method_info() const { return std::get<method_info>(info_); }
const namespace_info& get_namespace_info() const { return std::get<namespace_info>(info_); } bool is_function() const noexcept {
const variable_info& get_variable_info() const { return std::get<variable_info>(info_); } return std::holds_alternative<function_info>(info_);
}
bool is_method() const noexcept {
return std::holds_alternative<method_info>(info_);
}
bool is_namespace() const noexcept {
return std::holds_alternative<namespace_info>(info_);
}
bool is_variable() const noexcept {
return std::holds_alternative<variable_info>(info_);
}
std::optional<class_info> get_class() const {
if ( const class_info* info = std::get_if<class_info>(&info_); info ) {
return *info;
}
return std::nullopt;
}
std::optional<field_info> get_field() const {
if ( const field_info* info = std::get_if<field_info>(&info_); info ) {
return *info;
}
return std::nullopt;
}
std::optional<function_info> get_function() const {
if ( const function_info* info = std::get_if<function_info>(&info_); info ) {
return *info;
}
return std::nullopt;
}
std::optional<method_info> get_method() const {
if ( const method_info* info = std::get_if<method_info>(&info_); info ) {
return *info;
}
return std::nullopt;
}
std::optional<namespace_info> get_namespace() const {
if ( const namespace_info* info = std::get_if<namespace_info>(&info_); info ) {
return *info;
}
return std::nullopt;
}
std::optional<variable_info> get_variable() const {
if ( const variable_info* info = std::get_if<variable_info>(&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<decltype(info)>;
info.merge(std::get<info_type>(other.info_));
}
}, info_);
}
private: private:
std::variant< std::variant<
class_info, class_info,

View File

@@ -173,7 +173,7 @@ TEST_CASE("meta/examples/advanced") {
ivec3 v3{1,2,3}; ivec3 v3{1,2,3};
type v3_type = db.resolve(v3).value(); 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; std::cout << "class " << v3_info.id() << std::endl;

View File

@@ -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") { SUBCASE("class_template") {
CHECK(registry.resolve<ivec2>()); CHECK(registry.resolve<ivec2>());
const meta::type ivec2_type = registry.resolve<ivec2>().value(); const meta::type ivec2_type = registry.resolve<ivec2>().value();
CHECK(ivec2_type.is_class()); 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"); CHECK(ivec2_info.id() == "ivec2");
} }
@@ -85,7 +94,7 @@ TEST_CASE("meta/registry") {
const meta::type ivec3_type = registry.resolve(v3).value(); const meta::type ivec3_type = registry.resolve(v3).value();
CHECK(ivec3_type.is_class()); 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"); CHECK(ivec3_info.id() == "ivec3");
} }
@@ -95,7 +104,7 @@ TEST_CASE("meta/registry") {
const meta::type ivec2_x_type = registry.resolve<&ivec2::x>().value(); const meta::type ivec2_x_type = registry.resolve<&ivec2::x>().value();
CHECK(ivec2_x_type.is_field()); 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"); 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(); const meta::type ivec3_x_type = registry.resolve<&ivec3::x>().value();
CHECK(ivec3_x_type.is_field()); 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"); CHECK(ivec3_x_info.id() == "x");
} }
@@ -115,7 +124,7 @@ TEST_CASE("meta/registry") {
const meta::type iadd2_type = registry.resolve<&iadd2>().value(); const meta::type iadd2_type = registry.resolve<&iadd2>().value();
CHECK(iadd2_type.is_function()); 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"); CHECK(iadd2_info.id() == "iadd2");
} }
@@ -125,7 +134,7 @@ TEST_CASE("meta/registry") {
const meta::type iadd3_type = registry.resolve<&iadd3>().value(); const meta::type iadd3_type = registry.resolve<&iadd3>().value();
CHECK(iadd3_type.is_function()); 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"); CHECK(iadd3_info.id() == "iadd3");
} }
@@ -135,7 +144,7 @@ TEST_CASE("meta/registry") {
const meta::type ivec2_dot_type = registry.resolve<&ivec2::dot>().value(); const meta::type ivec2_dot_type = registry.resolve<&ivec2::dot>().value();
CHECK(ivec2_dot_type.is_method()); 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"); 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(); const meta::type ivec3_dot_type = registry.resolve<&ivec3::dot>().value();
CHECK(ivec3_dot_type.is_method()); 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"); 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(); const meta::type ivec2_zero_type = registry.resolve<&ivec2::zero>().value();
CHECK(ivec2_zero_type.is_variable()); 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"); 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(); const meta::type ivec3_zero_type = registry.resolve<&ivec3::zero>().value();
CHECK(ivec3_zero_type.is_variable()); 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"); 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>("ivec2")(
meta::field_<&ivec2::x>("x")
));
registry(
meta::class_<ivec2>("ivec2")(
meta::field_<&ivec2::y>("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.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>("ivec2")));
registry(
meta::namespace_("vmath")(
meta::class_<ivec3>("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"));
}
}

View File

@@ -33,25 +33,44 @@ TEST_CASE("meta/type") {
meta::type class_type = meta::class_<clazz>("clazz"); meta::type class_type = meta::class_<clazz>("clazz");
CHECK(class_type.is_class()); 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"); meta::type field_type = meta::field_<&clazz::field>("field");
CHECK(field_type.is_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"); meta::type function_type = meta::function_<&clazz::function>("function");
CHECK(function_type.is_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"); meta::type method_type = meta::method_<&clazz::method>("method");
CHECK(method_type.is_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"); meta::type namespace_type = meta::namespace_("ns");
CHECK(namespace_type.is_namespace()); 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"); meta::type variable_type = meta::variable_<&clazz::variable>("variable");
CHECK(variable_type.is_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>("clazz")(
meta::field_<&clazz::field>("field")
);
clazz_type.merge(meta::class_<clazz>("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"));
}
} }