Skip to content

Latest commit

 

History

History
226 lines (189 loc) · 6.56 KB

359.md

File metadata and controls

226 lines (189 loc) · 6.56 KB
Info

Example

namespace detail {
template <class, auto>
[[nodiscard]] consteval auto member_name() -> std::string_view {
    return std::source_location::current().function_name();
}
template <class T> extern const T external;
consteval auto get(auto& obj) {
    auto& [p1] = obj;
    return &p1;
}
} // namespace deatil

template <class T>
constexpr auto member_name = detail::member_name<T, detail::get(detail::external<T>)>();

struct foo {
  int bar;
};

static_assert(member_name<foo>.find("bar") != std::string_view::npos);

https://godbolt.org/z/sMKMWa35W

Puzzle

  • Can you implement to_tuple which returns a tuple from a struct with names?
template <fixed_string Name, class T>
struct named {
    static constexpr auto name = Name;
    T value{};
};

[[nodiscard]] consteval auto to_tuple(auto&& t); // TODO

struct foo {
    int a;
    int b;
};

constexpr auto t = to_tuple(foo{.a=42, .b=87});
static_assert("a" == std::get<0>(t).name and 42 == std::get<0>(t).value);
static_assert("b" == std::get<1>(t).name and 87 == std::get<1>(t).value);

https://godbolt.org/z/fWxPdj7v6

Solutions

template <std::size_t N>
class fixed_string final {
   public:
    constexpr explicit(true) fixed_string(const auto... cs) : data{cs...} {}
    constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
        std::copy_n(str, N + 1, std::data(data));
    }
    [[nodiscard]] constexpr auto operator<=>(const fixed_string&) const =
        default;
    [[nodiscard]] constexpr explicit(false) operator std::string_view() const {
        return {std::data(data), N};
    }
    [[nodiscard]] constexpr auto size() const -> std::size_t { return N; }
    std::array<char, N + 1> data{};
};

template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;

template <fixed_string Name, class T>
struct named {
    static constexpr auto name = Name;
    T value{};
};

struct any_type {
    template <class T>
    constexpr operator T();
};

template <class TPtr>
struct ptr {
    const TPtr* ptr;
};

namespace detail {
template <class T>
extern const T external;
struct any_type {
    template <class T>
    constexpr operator T();
};
template <class TPtr>
struct ptr {
    const TPtr* ptr;
};

template <auto N, class T>
[[nodiscard]] constexpr auto nth_ptr(T&& t) {
    if constexpr (requires { T{any_type{}, any_type{}, any_type{}}; }) {
        auto&& [p1, p2, p3] = t;
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
        if constexpr (N == 1) return ptr<decltype(p2)>{&p2};
        if constexpr (N == 2) return ptr<decltype(p3)>{&p3};
    } else if constexpr (requires { T{any_type{}, any_type{}}; }) {
        auto&& [p1, p2] = t;
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
        if constexpr (N == 1) return ptr<decltype(p2)>{&p2};
    } else if constexpr (requires { T{any_type{}}; }) {
        auto&& [p1] = t;
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
    }
}

template <auto Ptr>
[[nodiscard]] consteval auto get_name() -> std::string_view {
    return std::source_location::current().function_name();
}

template <auto N, class T>
constexpr auto get_name_impl =
    detail::get_name<detail::nth_ptr<N>(detail::external<T>)>();

struct $struct$ {
    int $field$;
};
constexpr auto $name = get_name_impl<0, detail::$struct$>;
constexpr auto $end =
    $name.substr($name.find("$field$") + sizeof("$field$") - 1);
constexpr auto $begin = $name[$name.find("$field$") - 1];
}  // namespace detail

template <auto N, class T>
constexpr auto get_name = [] {
    const auto name = detail::get_name_impl<N, T>;
    const auto begin = name.find(detail::$end);
    const auto tmp = name.substr(0, begin);
    return tmp.substr(tmp.find_last_of(detail::$begin) + 1);
}();

template <auto N>
[[nodiscard]] consteval auto nth(auto... args) {
    return [&]<std::size_t... Ns>(std::index_sequence<Ns...>) {
        return [](decltype((void*)Ns)..., auto* nth, auto*...) {
            return *nth;
        }(&args...);
    }(std::make_index_sequence<N>{});
}

template <auto N, class T>
constexpr auto get(T&& t) {
    if constexpr (requires { T{any_type{}, any_type{}, any_type{}}; }) {
        auto&& [p1, p2, p3] = t;
        // structure bindings is not constexpr :/
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
        if constexpr (N == 1) return ptr<decltype(p2)>{&p2};
        if constexpr (N == 2) return ptr<decltype(p3)>{&p3};
    } else if constexpr (requires { T{any_type{}, any_type{}}; }) {
        auto&& [p1, p2] = t;
        // structure bindings is not constexpr :/
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
        if constexpr (N == 1) return ptr<decltype(p2)>{&p2};
    } else if constexpr (requires { T{any_type{}}; }) {
        auto&& [p1] = t;
        // structure bindings is not constexpr :/
        if constexpr (N == 0) return ptr<decltype(p1)>{&p1};
    }
}

template <class T, auto N>
[[nodiscard]] consteval auto member_name() {
    constexpr auto name = get_name<N, T>;
    return [&]<auto... Ns>(std::index_sequence<Ns...>) {
        return fixed_string<sizeof...(Ns)>{name[Ns]...};
    }(std::make_index_sequence<name.size()>{});
}

template <class T>
[[nodiscard]] constexpr auto to_tuple(const T& t) {
    if constexpr (requires { T{any_type{}, any_type{}, any_type{}}; }) {
        auto&& [p1, p2, p3] = t;
        return std::tuple(
            named<member_name<T, 0>(), decltype(p1)>{.value = p1},
            named<member_name<T, 1>(), decltype(p2)>{.value = p2},
            named<member_name<T, 2>(), decltype(p2)>{.value = p3});
    } else if constexpr (requires { T{any_type{}, any_type{}}; }) {
        auto&& [p1, p2] = t;
        return std::tuple(
            named<member_name<T, 0>(), decltype(p1)>{.value = p1},
            named<member_name<T, 1>(), decltype(p2)>{.value = p2});
    } else if constexpr (requires { T{any_type{}}; }) {
        auto&& [p1] = t;
        return std::tuple(
            named<member_name<T, 0>(), decltype(p1)>{.value = p1});
    } else {
        return std::tuple();
    }
}

struct foo {
    int a;
    int b;
};

constexpr auto t = to_tuple(foo{.a=42, .b=87});
static_assert("a" == std::get<0>(t).name and 42 == std::get<0>(t).value);
static_assert("b" == std::get<1>(t).name and 87 == std::get<1>(t).value);

https://godbolt.org/z/beq5reqdE