Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type_name #2

Merged
merged 3 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class T>
constexpr std::string_view type_name;
```

Get the name of the type `T`.

<details>
<summary>Example</summary>

```cpp
#include <field_reflection.hpp>
#include <format>
#include <type_traits>
#include <utility> // std::exchange
#include <variant>

using Token = std::variant<struct Number, struct Identifier>;

struct Number {
int value;
};

struct Identifier {
std::string name;
};

template <typename T, typename Variant>
inline constexpr bool alternative_of = false;

template <typename T, typename... Types>
inline constexpr bool alternative_of<T, std::variant<Types...>> =
(std::is_same_v<T, Types> || ...);

template <typename T>
requires alternative_of<T, Token>
struct std::formatter<T> {
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<T>);
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 <iostream>

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)
</details>

### `for_each_field`, `all_of_field`, `any_of_field`

```cpp
Expand Down
52 changes: 52 additions & 0 deletions include/field_reflection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,17 @@ namespace field_reflection
#endif
}

template <typename T>
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 <typename T, auto Ptr>
consteval std::string_view get_field_name()
{
Expand Down Expand Up @@ -646,6 +657,46 @@ namespace field_reflection
template <field_referenceable T, std::size_t N>
using field_type = remove_rvalue_reference_t<decltype(std::get<N>(to_tuple(std::declval<T&>())))>;

struct type_name_detector
{
};

template <typename T>
consteval std::string_view get_type_name()
{
#if defined(__GNUC__) || defined(__clang__)
constexpr auto detector_name = get_function_name<type_name_detector>();
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<T>();
return type_name_raw.substr(dummy_begin, type_name_raw.size() - dummy_begin - dummy_suffix_length);
#else
constexpr auto detector_name = get_function_name<type_name_detector>();
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<T>();
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 <class T>
constexpr std::string_view type_name = get_type_name<T>();

template <std::size_t N, typename T, field_referenceable U = std::remove_cvref_t<T>>
constexpr decltype(auto) get_field(T& t) noexcept
{
Expand Down Expand Up @@ -773,6 +824,7 @@ namespace field_reflection
using detail::field_type;
using detail::get_field;
using detail::to_tuple;
using detail::type_name;

template <typename T1, typename T2, typename Func, field_referenceable U1 = std::remove_cvref_t<T1>,
field_referenceable U2 = std::remove_cvref_t<T2>>
Expand Down
11 changes: 11 additions & 0 deletions test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ TEST(field_reflection, field_name)
static_assert(field_name<my_struct11, 1> == "y1");
}

TEST(field_reflection, type_name)
{
static_assert(type_name<my_struct1> == "my_struct1");
static_assert(type_name<named::my_struct10> == "named::my_struct10");
#if defined(__GNUC__) || defined(__clang__)
static_assert(type_name<std::pair<int, double>> == "std::pair<int, double>");
#else
static_assert(type_name<std::pair<int, double>> == "std::pair<int,double>");
#endif
}

TEST(field_reflection, get_field)
{
{
Expand Down