From e881bc152764bf4047d8e1318a99587c2f339d5d Mon Sep 17 00:00:00 2001 From: acd1034 <48613285+acd1034@users.noreply.github.com> Date: Fri, 13 Dec 2024 19:35:13 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20`type=5Fname`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/field_reflection.hpp | 52 ++++++++++++++++++++++++++++++++++++ test/test.cpp | 7 +++++ 2 files changed, 59 insertions(+) diff --git a/include/field_reflection.hpp b/include/field_reflection.hpp index b260c06..6a20de3 100644 --- a/include/field_reflection.hpp +++ b/include/field_reflection.hpp @@ -612,6 +612,17 @@ namespace field_reflection #endif } + template + consteval std::string_view get_function_name() + { +#if defined(__clang__) && defined(_WIN32) + // clang-cl returns function_name() as __FUNCTION__ instead of __PRETTY_FUNCTION__ + return std::string_view{__PRETTY_FUNCTION__}; +#else + return std::string_view{std::source_location::current().function_name()}; +#endif + } + template consteval std::string_view get_field_name() { @@ -646,6 +657,46 @@ namespace field_reflection template using field_type = remove_rvalue_reference_t(to_tuple(std::declval())))>; + struct type_name_detector + { + }; + + template + consteval std::string_view get_type_name() + { +#if defined(__GNUC__) || defined(__clang__) + constexpr auto detector_name = get_function_name(); + constexpr auto dummy = std::string_view("T = "); + constexpr auto dummy_begin = detector_name.find(dummy) + dummy.size(); + constexpr auto dummy2 = std::string_view("type_name_detector"); + constexpr auto dummy_suffix_length = detector_name.size() - detector_name.find(dummy2) - dummy2.size(); + + constexpr auto type_name_raw = get_function_name(); + return type_name_raw.substr(dummy_begin, type_name_raw.size() - dummy_begin - dummy_suffix_length); +#else + constexpr auto detector_name = get_function_name(); + constexpr auto dummy = std::string_view("struct field_reflection::detail::type_name_detector"); + constexpr auto dummy_begin = detector_name.find(dummy); + constexpr auto dummy_suffix_length = detector_name.size() - dummy_begin - dummy.size(); + + auto type_name_raw = get_function_name(); + auto type_name = + type_name_raw.substr(dummy_begin, type_name_raw.size() - dummy_begin - dummy_suffix_length); + if (auto s = std::string_view("struct "); type_name.starts_with(s)) + { + type_name.remove_prefix(s.size()); + } + if (auto s = std::string_view("class "); type_name.starts_with(s)) + { + type_name.remove_prefix(s.size()); + } + return type_name; +#endif + } + + template + constexpr std::string_view type_name = get_type_name(); + template > constexpr decltype(auto) get_field(T& t) noexcept { @@ -773,6 +824,7 @@ namespace field_reflection using detail::field_type; using detail::get_field; using detail::to_tuple; + using detail::type_name; template , field_referenceable U2 = std::remove_cvref_t> diff --git a/test/test.cpp b/test/test.cpp index e51597b..546c9fa 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -196,6 +196,13 @@ TEST(field_reflection, field_name) static_assert(field_name == "y1"); } +TEST(field_reflection, type_name) +{ + static_assert(type_name == "my_struct1"); + static_assert(type_name == "named::my_struct10"); + static_assert(type_name> == "std::pair"); +} + TEST(field_reflection, get_field) { { From 87b35d3a9e39157f552bf350c325c9d1fe11a27e Mon Sep 17 00:00:00 2001 From: acd1034 <48613285+acd1034@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:47:47 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=85=20Fix=20test=20for=20MSVC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test.cpp b/test/test.cpp index 546c9fa..8916e4e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -200,7 +200,11 @@ TEST(field_reflection, type_name) { static_assert(type_name == "my_struct1"); static_assert(type_name == "named::my_struct10"); +#if defined(__GNUC__) || defined(__clang__) static_assert(type_name> == "std::pair"); +#else + static_assert(type_name> == "std::pair"); +#endif } TEST(field_reflection, get_field) From 3a90866384d7409bc211f99c5ba471090fc54fb1 Mon Sep 17 00:00:00 2001 From: acd1034 <48613285+acd1034@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:49:25 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=93=9D=20Add=20description=20for=20`t?= =?UTF-8?q?ype=5Fname`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/README.md b/README.md index e73f94a..92fe35f 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,88 @@ constexpr auto get_field(T&& t) noexcept; Extracts the `N`-th element from the `field_referenceable` type `T` and returns a reference to it. It behaves like `std::get` for `std::tuple` but returns a lvalue value instead of a rvalue reference. +### `type_name` + +```cpp +template +constexpr std::string_view type_name; +``` + +Get the name of the type `T`. + +
+Example + +```cpp +#include +#include +#include +#include // std::exchange +#include + +using Token = std::variant; + +struct Number { + int value; +}; + +struct Identifier { + std::string name; +}; + +template +inline constexpr bool alternative_of = false; + +template +inline constexpr bool alternative_of> = + (std::is_same_v || ...); + +template + requires alternative_of +struct std::formatter { + constexpr auto parse(auto& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() and *it != '}') { + throw std::format_error("invalid format"); + } + return it; + } + + auto format(const T& t, auto& ctx) const -> decltype(ctx.out()) { + auto out = ctx.out(); + out = std::format_to(out, "{} {{", field_reflection::type_name); + const char* dlm = ""; + field_reflection::for_each_field( + t, [&](std::string_view name, const auto& value) { + std::format_to( + out, "{}\n .{}={}", std::exchange(dlm, ","), name, value); + }); + out = std::format_to(out, "\n}}"); + return out; + } +}; + +#include + +int main() { + Number num{42}; + Identifier ident{"ident"}; + std::cout << std::format("{}", num) << std::endl; + std::cout << std::format("{}", ident) << std::endl; + // Expected Output + // =============== + // Number { + // .value=42 + // } + // Identifier { + // .name=ident + // } +} +``` + +🔗[Execution example in Compiler Explorer](https://godbolt.org/z/94fPc895o) +
+ ### `for_each_field`, `all_of_field`, `any_of_field` ```cpp