diff --git a/.clang-format b/.clang-format index 73fbcbc..2f98109 100644 --- a/.clang-format +++ b/.clang-format @@ -27,6 +27,8 @@ DerivePointerAlignment: 'false' IncludeCategories: - Regex: '.*\.tinyrefl' Priority: 10000 + - Regex: 'tinyrefl/entities.hpp' + Priority: 20000 IndentCaseLabels: 'false' IndentWidth: '4' IndentWrappedFunctionNames: 'true' diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 26e2b64..9798a6f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,7 +1,7 @@ include(external/external.cmake) add_library(tinyrefl-example-lib example.cpp) -tinyrefl_tool(TARGET tinyrefl-example-lib HEADERS example.hpp) +tinyrefl_tool(TARGET tinyrefl-example-lib HEADERS example.hpp example2.hpp) add_tinyrefl_example(tinyrefl-example api.cpp) diff --git a/examples/api.cpp b/examples/api.cpp index d54db81..a0dbeb6 100644 --- a/examples/api.cpp +++ b/examples/api.cpp @@ -1,8 +1,11 @@ #include "example.hpp" +#include "example2.hpp" #include #include #include #include "example.hpp.tinyrefl" +#include "example2.hpp.tinyrefl" +#include /* @@ -203,4 +206,41 @@ int main() .get_attribute(0) == "A", "Expected [[A]] as first attribute"); #endif // TINYREFL_HAS_ENUM_VALUE_ATTRIBUTES + + // Print all reflected entities + tinyrefl::visit_entities( + [](auto /* display_name */, auto index, auto entity, auto /* kind */) { + using Index = decltype(index); + using Entity = decltype(entity); + + std::cout << "[entity " << Index::value << "] " << Entity::kind + << " '" << tinyrefl::full_display_name() << "'\n"; + }); + + // Print all enums + tinyrefl::visit_enums([](auto /* display_name */, auto entity) { + using Entity = decltype(entity); + + std::cout << "enum " << Entity::name.full_name() << "\n"; + + for(const auto value : entity) + { + std::cout << " - " << value.name() << "\n"; + } + }); + + // Print all classes + tinyrefl::visit_classes([](auto /* display_name */, auto entity) { + using Entity = decltype(entity); + + std::cout << "class " << Entity::name.full_name() << "\n"; + }); + + // Print all member variables + tinyrefl::visit_entities( + [](auto /* display_name */, auto entity) { + using Entity = decltype(entity); + + std::cout << "member variable " << Entity::name.full_name() << "\n"; + }); } diff --git a/examples/example2.hpp b/examples/example2.hpp new file mode 100644 index 0000000..ec3d54b --- /dev/null +++ b/examples/example2.hpp @@ -0,0 +1,64 @@ +#ifndef TINYREFL_EXAMPLE2_HPP +#define TINYREFL_EXAMPLE2_HPP + +#include +#include + +namespace example2 +{ + +struct A +{ + int a = 42; +}; + + +struct B +{ + const char* b = "hello"; +}; + +enum class Enum +{ + A, + B, + C, + D, + E = 42 +}; + +struct C : public A, public B +{ +public: + [[tinyrefl::ignore]] std::string ignore_me; + std::string hey_im_here = "hey, I'm here"; + B subobject; + + C() = default; + explicit C(const std::string& str) {} + [[C]] C(int a, int b) {} + + [[f, f1, f2]] void f(int a, int b) const {}; + [[f]] void f(int a, int b){}; + [[f]] void f(){}; + [[f]] void f() const {}; + + enum class Enum + { + A TINYREFL_ENUM_VALUE_ATTRIBUTE(A), + B TINYREFL_ENUM_VALUE_ATTRIBUTE(B), + C TINYREFL_ENUM_VALUE_ATTRIBUTE(C), + D TINYREFL_ENUM_VALUE_ATTRIBUTE(D), + E TINYREFL_ENUM_VALUE_ATTRIBUTE(E), + F TINYREFL_ENUM_VALUE_ATTRIBUTE(F) + }; + + [[e]] Enum e = Enum::A; +}; + +std::ostream& operator<<(std::ostream& os, const B& b); +std::ostream& operator<<(std::ostream& os, const Enum& value); +std::ostream& operator<<(std::ostream& os, const C::Enum& value); +} // namespace example2 + +#endif // TINYREFL_EXAMPLE2_HPP diff --git a/examples/rttr.hpp b/examples/rttr.hpp index 6166a10..d06f137 100644 --- a/examples/rttr.hpp +++ b/examples/rttr.hpp @@ -57,7 +57,7 @@ auto register_rttr_type() -> std::enable_if_t< std::is_enum::value && tinyrefl::has_metadata()> { register_rttr_enum(rttr::registration::enumeration( - tinyrefl::metadata().name().full_name().begin())); + tinyrefl::metadata::name.full_name().begin())); } template @@ -66,7 +66,7 @@ auto register_rttr_member_enum(rttr::registration::class_& registration) std::is_enum::value && tinyrefl::has_metadata()> { register_rttr_enum(registration.template enumeration( - tinyrefl::metadata().name().full_name().begin())); + tinyrefl::metadata::name.full_name().begin())); } template diff --git a/include/tinyrefl/api.hpp b/include/tinyrefl/api.hpp index f6d0b68..8ac89b9 100644 --- a/include/tinyrefl/api.hpp +++ b/include/tinyrefl/api.hpp @@ -15,6 +15,12 @@ namespace tinyrefl using entity = tinyrefl::backend::entity_kind; +template +using kind_tag = TINYREFL_STATIC_VALUE(Kind); + +template +constexpr kind_tag kind_tag_value{}; + template using type_tag = ctti::type_tag; diff --git a/include/tinyrefl/backend.hpp b/include/tinyrefl/backend.hpp index 911b41e..91ded8f 100644 --- a/include/tinyrefl/backend.hpp +++ b/include/tinyrefl/backend.hpp @@ -814,7 +814,7 @@ struct enum_, Attributes> : public metadata_with_attributes { static constexpr entity_kind kind = entity_kind::ENUM; - static constexpr ctti::name_t enum_name = + static constexpr ctti::name_t name = tinyrefl::backend::string_constant(); using values = tinyrefl::meta::list; using enum_type = Enum; @@ -932,9 +932,9 @@ struct enum_, Attributes> return values::size; } - constexpr const ctti::name_t& name() const + constexpr const ctti::name_t& name_() const { - return enum_::enum_name; + return enum_::name; } constexpr const value_t& get_value(const ctti::detail::cstring& name) const @@ -1021,19 +1021,13 @@ constexpr enum_values; template constexpr ctti::name_t - enum_, Attributes>::enum_name; + enum_, Attributes>::name; template constexpr typename enum_, Attributes>:: value_t enum_, Attributes>:: invalid_value; -template -constexpr ctti::detail::cstring display_name(Entity = Entity{}) -{ - return Entity::name.name(); -} - template struct has_custom_display_name : tinyrefl::meta::bool_< @@ -1042,30 +1036,34 @@ struct has_custom_display_name }; template -constexpr auto full_display_name(Entity = Entity{}) -> std:: +constexpr auto full_display_name() -> std:: enable_if_t::value, ctti::detail::cstring> { + static_assert(std::is_class::value, ""); return Entity::name.full_name(); } template -constexpr auto display_name(Entity = Entity{}) -> std:: +constexpr auto display_name() -> std:: enable_if_t::value, ctti::detail::cstring> { + static_assert(std::is_class::value, ""); return Entity::name.name(); } template -constexpr auto full_display_name(Entity = Entity{}) -> std:: +constexpr auto full_display_name() -> std:: enable_if_t::value, ctti::detail::cstring> { + static_assert(std::is_class::value, ""); return Entity::full_display_name; } template -constexpr auto display_name(Entity = Entity{}) -> std:: +constexpr auto display_name() -> std:: enable_if_t::value, ctti::detail::cstring> { + static_assert(std::is_class::value, ""); return Entity::display_name; } @@ -1109,6 +1107,8 @@ constexpr auto display_name(Entity = Entity{}) -> std:: #define TINYREFL_SEQUENCE(elems) \ ::tinyrefl::meta::list +#define TINYREFL_SEQUENCE_CAT(x, y) \ + ::tinyrefl::meta::cat_t #define TINYREFL_TYPE(name, fullname) TINYREFL_PP_UNWRAP fullname #define TINYREFL_VALUE(type, value) \ ::ctti::static_value diff --git a/include/tinyrefl/entities.hpp b/include/tinyrefl/entities.hpp new file mode 100644 index 0000000..ccbca6e --- /dev/null +++ b/include/tinyrefl/entities.hpp @@ -0,0 +1,73 @@ +#ifndef TINYREFL_ENTITIES_HPP_INCLUDED +#define TINYREFL_ENTITIES_HPP_INCLUDED + +#include + +#if !defined(TINYREFL_API_HPP) || !defined(TINYREFL_ENTITIES) +#error \ + "To use this header, first include your reflected headers, then , then your reflected headers generated code (.tinyrefl header files), and then this header" +#endif + +namespace tinyrefl +{ + +using entities = tinyrefl::meta::fmap_t< + tinyrefl::meta::defer, + TINYREFL_ENTITIES>; + +template +constexpr ctti::detail::cstring display_name(Entity entity = Entity{}) +{ + static_assert(std::is_class::value, ""); + return tinyrefl::backend::display_name(); +} + +template +constexpr ctti::detail::cstring full_display_name(Entity entity = Entity{}) +{ + static_assert(std::is_class::value, ""); + return tinyrefl::backend::full_display_name(); +} + +template +void visit_entities(Visitors... visitors) +{ + tinyrefl::meta::foreach( + [visitor = tinyrefl::overloaded_function_default(visitors...)]( + auto type, auto index) { + using Entity = typename decltype(type)::type; + + static_assert(std::is_class::value, ""); + + visitor( + tinyrefl::display_name(), + index, + Entity{}, + kind_tag_value); + }); +} + +template +void visit_entities(Visitor visitor) +{ + visit_entities( + [visitor]( + auto display_name, auto /* index */, auto entity, kind_tag) { + visitor(display_name, entity); + }); +} + +template +void visit_classes(Visitor visitor) +{ + visit_entities(visitor); +} + +template +void visit_enums(Visitor visitor) +{ + visit_entities(visitor); +} +} // namespace tinyrefl + +#endif // TINYREFL_ENTITIES_HPP_INCLUDED diff --git a/tests/static/api.cpp b/tests/static/api.cpp index 53708c2..5b151a0 100644 --- a/tests/static/api.cpp +++ b/tests/static/api.cpp @@ -4,8 +4,13 @@ #include #include "../example.hpp.tinyrefl" #include "members.hpp.tinyrefl" +#include #include CTTI_STATIC_TESTS_HEADER +#if TINYREFL_GENERATED_FILE_COUNT != 2 +#error "Expected two tinyrefl codegen headers" +#endif + using check = tinyrefl::test::AssertMetadataAvailableForTemplateParam; EXPECT_TRUE(std::is_pointer::value); EXPECT_FALSE(tinyrefl::has_attribute("attribute")); @@ -298,3 +303,14 @@ EXPECT_EQ( tinyrefl::entity_metadata< "my_namespace::MyClass::overloaded(int)"_id>::arg_names[0], "firstArg"); + +EXPECT_TRUE(tinyrefl::has_entity_metadata<"my_namespace::MyClass::Enum"_id>()); +EXPECT_TRUE(std::is_same< + tinyrefl::entity_metadata<"my_namespace::MyClass::Enum"_id>, + tinyrefl::metadata>::value); + +EXPECT_TRUE( + std::is_same< + tinyrefl::meta:: + filter_t, tinyrefl::entities>, + tinyrefl::entities>::value); diff --git a/tool/tool.cpp b/tool/tool.cpp index 62af890..e364f41 100644 --- a/tool/tool.cpp +++ b/tool/tool.cpp @@ -78,14 +78,18 @@ std::string sequence( { std::ostringstream os; - for(std::size_t i = 0; i < elems.size(); ++i) + std::size_t i = 0; + + for(const auto& elem : elems) { - os << prefix << elems[i] << suffix; + os << prefix << elem << suffix; if(i < elems.size() - 1) { os << separator; } + + i++; } return os.str(); @@ -257,7 +261,8 @@ std::string full_qualified_display_name(const cppast::cpp_entity& entity) } } -std::string typelist(const std::vector& args) +template +std::string typelist(const Sequence& args) { return fmt::format("TINYREFL_SEQUENCE(({}))", sequence(args, ", ")); } @@ -563,6 +568,24 @@ std::string constructor(const cppast::cpp_constructor& ctor) attributes(ctor)); } +static std::unordered_set entities; + +template +void register_entity(const Entity& entity) +{ + bool already_registered = + !entities.insert(string_constant(full_qualified_display_name(entity))) + .second; + + if(already_registered) + { + fmt::print( + stderr, + "WARNING: An entity named \"{}\" already exists!\n", + full_qualified_display_name(entity)); + } +} + void generate_class(std::ostream& os, const cppast::cpp_class& class_) { std::vector member_variables; @@ -575,6 +598,7 @@ void generate_class(std::ostream& os, const cppast::cpp_class& class_) std::cout << " # " << full_qualified_name(class_) << " [attributes: " << sequence(class_.attributes(), ", ", "\"", "\"") << "]\n"; + register_entity(class_); cppast::visit( class_, @@ -606,6 +630,8 @@ void generate_class(std::ostream& os, const cppast::cpp_class& class_) static_cast(child)); member_functions.push_back(member); generate_member(os, member); + register_entity( + static_cast(child)); break; } case cppast::cpp_entity_kind::member_variable_t: @@ -619,6 +645,8 @@ void generate_class(std::ostream& os, const cppast::cpp_class& class_) static_cast(child)); member_variables.push_back(member); generate_member(os, member); + register_entity( + static_cast(child)); break; } case cppast::cpp_entity_kind::class_t: @@ -706,6 +734,8 @@ void generate_enum(std::ostream& os, const cppast::cpp_enum& enum_) { std::vector values; + register_entity(enum_); + cppast::visit( enum_, [&values, &os]( @@ -721,7 +751,7 @@ void generate_enum(std::ostream& os, const cppast::cpp_enum& enum_) << sequence(value.attributes(), ", ", "\"", "\"") << "]\n"; - + register_entity(value); generate_enum_value(os, value); values.push_back(enum_value(value)); } @@ -737,6 +767,50 @@ void generate_enum(std::ostream& os, const cppast::cpp_enum& enum_) } } +void generate_global_metadata_list(std::ostream& os) +{ + const auto entities_sequence = typelist(entities); + + os << "#ifndef TINYREFL_GENERATED_FILE_COUNT\n" + " #define TINYREFL_GENERATED_FILE_COUNT 0\n" + " #define TINYREFL_ENTITIES_0 " + << entities_sequence + << "\n" + " #define TINYREFL_ENTITIES TINYREFL_ENTITIES_0\n" + "#endif // TINYREFL_GENERATED_FILE_COUNT\n" + "\n"; + + static constexpr int TINYREFL_TOOL_MAX_GENERATED_FILES = 128; + + for(int i = 0; i <= TINYREFL_TOOL_MAX_GENERATED_FILES; ++i) + { + if(i < TINYREFL_TOOL_MAX_GENERATED_FILES) + { + fmt::print( + os, + "#{if} TINYREFL_GENERATED_FILE_COUNT == {i}\n" + " #undef TINYREFL_GENERATED_FILE_COUNT\n" + " #define TINYREFL_GENERATED_FILE_COUNT {next}\n" + " #define TINYREFL_ENTITIES_{next} TINYREFL_SEQUENCE_CAT((TINYREFL_ENTITIES_{i}), ({entities}))\n" + " #undef TINYREFL_ENTITIES\n" + " #define TINYREFL_ENTITIES TINYREFL_ENTITIES_{next}\n", + fmt::arg("if", (i == 0 ? "if" : "elif")), + fmt::arg("i", i), + fmt::arg("next", i + 1), + fmt::arg("entities", entities_sequence)); + } + else + { + fmt::print( + os, + "#else\n" + " #error Only up to {} tinyrefl generated code headers can be included in the same translation unit\n" + "#endif // TINYREFL_GENERATED_FILE_COUNT\n\n", + TINYREFL_TOOL_MAX_GENERATED_FILES); + } + } +} + void visit_ast_and_generate( const cppast::cpp_file& ast_root, const std::string& filepath) @@ -799,6 +873,7 @@ void visit_ast_and_generate( generate_string_definitions(os); os << body.str(); + generate_global_metadata_list(os); os << "\n#undef TINYREFL_TOOL_CODEGEN_VERSION_MAJOR\n" "#undef TINYREFL_TOOL_CODEGEN_VERSION_MINOR\n"