include/ylt/reflection/member_value.hpp (240 lines of code) (raw):
#pragma once
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <variant>
#include "member_names.hpp"
#include "template_string.hpp"
#include "template_switch.hpp"
namespace ylt::reflection {
namespace internal {
template <typename Member, typename T>
inline void set_member_ptr(Member& member, T t) {
if constexpr (std::is_constructible_v<Member, T>) {
member = t;
}
else {
std::string str = "given type: ";
str.append(type_string<std::remove_pointer_t<Member>>());
str.append(" is not equal with real type: ")
.append(type_string<std::remove_pointer_t<T>>());
throw std::invalid_argument(str);
}
}
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
struct switch_helper {
template <size_t index, typename Member, class Tuple>
static constexpr size_t run(Member& member, Tuple& t) {
if constexpr (index >= std::tuple_size_v<Tuple>) {
return index;
}
else {
if constexpr (is_variant<Member>::value) {
member = Member{std::in_place_index<index>, &std::get<index>(t)};
}
else {
set_member_ptr(member, &std::get<index>(t));
}
return index;
}
}
};
inline constexpr frozen::string filter_str(const frozen::string& str) {
if (str.size() > 3 && str[0] == '_' && str[1] == '_' && str[2] == '_') {
auto ptr = str.data() + 3;
return frozen::string(ptr, str.size() - 3);
}
return str;
}
template <typename value_type>
struct offset_t {
using type = value_type;
size_t value;
};
template <typename T, typename Tuple, size_t... Is>
inline auto get_variant_type() {
return std::variant<offset_t<
ylt::reflection::remove_cvref_t<std::tuple_element_t<Is, Tuple>>>...>{};
}
template <typename T, size_t... Is>
inline constexpr auto get_variant_map_impl(std::index_sequence<Is...>) {
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto arr = ylt::reflection::get_member_names<U>();
auto& offset_arr = get_member_offset_arr(wrapper<U>::value);
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<U>()));
using ValueType = decltype(get_variant_type<U, Tuple, Is...>());
return frozen::unordered_map<frozen::string, ValueType, sizeof...(Is)>{
{filter_str(arr[Is]),
ValueType{std::in_place_index<Is>,
offset_t<ylt::reflection::remove_cvref_t<
std::tuple_element_t<Is, Tuple>>>{offset_arr[Is]}}}...};
}
} // namespace internal
template <typename T>
inline constexpr auto get_variant_map() {
return internal::get_variant_map_impl<T>(
std::make_index_sequence<members_count_v<T>>{});
}
template <typename Member, typename T>
inline Member& get(T& t, size_t index) {
auto ref_tp = object_to_tuple(t);
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
if (index >= tuple_size) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(tuple_size));
throw std::out_of_range(str);
}
Member* member_ptr = nullptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return *member_ptr;
}
template <typename Member, typename T>
inline Member& get(T& t, std::string_view name) {
static constexpr auto map = member_names_map<T>;
size_t index = map.at(name); // may throw out_of_range: unknown key.
auto ref_tp = object_to_tuple(t);
Member* member_ptr = nullptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return *member_ptr;
}
template <typename T>
inline auto get(T& t, size_t index) {
auto ref_tp = object_to_tuple(t);
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
if (index >= tuple_size) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(tuple_size));
throw std::out_of_range(str);
}
using variant = decltype(tuple_to_variant(ref_tp));
variant member_ptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return member_ptr;
}
template <typename T>
inline constexpr auto get(T& t, std::string_view name) {
constexpr auto& map = member_names_map<T>;
size_t index = map.at(name); // may throw out_of_range: unknown key.
return get(t, index);
}
template <size_t index, typename T>
inline constexpr auto& get(T& t) {
auto ref_tp = object_to_tuple(t);
static_assert(index < std::tuple_size_v<decltype(ref_tp)>,
"index out of range");
return std::get<index>(ref_tp);
}
#if __cplusplus >= 202002L
template <FixedString name, typename T>
inline constexpr auto& get(T& t) {
constexpr size_t index = index_of<T, name>();
return get<index>(t);
}
#endif
template <typename T, typename Field>
inline size_t index_of(T& t, Field& value) {
const auto& offset_arr = member_offsets<T>;
size_t cur_offset = (const char*)(&value) - (const char*)(&t);
auto it = std::lower_bound(offset_arr.begin(), offset_arr.end(), cur_offset);
if (it == offset_arr.end()) {
return offset_arr.size();
}
return std::distance(offset_arr.begin(), it);
}
template <typename Member>
inline size_t index_of(Member member) {
using T = typename member_traits<Member>::owner_type;
static auto& t = internal::get_fake_object<T>();
return index_of(t, t.*member);
}
template <typename T, typename Field>
inline constexpr std::string_view name_of(T& t, Field& value) {
size_t index = index_of(t, value);
constexpr auto arr = get_member_names<T>();
if (index == arr.size()) {
return "";
}
return arr[index];
}
template <typename T, typename Visit, size_t... Is, typename... Args>
inline constexpr void visit_members_impl0(Visit&& func,
std::index_sequence<Is...>,
Args&... args) {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is]), ...);
}
template <typename T, typename Visit, size_t... Is, typename... Args>
inline constexpr void visit_members_impl(Visit&& func,
std::index_sequence<Is...>,
Args&... args) {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is], Is), ...);
}
template <typename T, typename Visit>
inline constexpr void for_each(T&& t, Visit&& func) {
using Tuple = decltype(object_to_tuple(t));
using first_t = std::tuple_element_t<0, Tuple>;
if constexpr (std::is_invocable_v<Visit, first_t>) {
visit_members(t, [&func](auto&... args) {
(func(args), ...);
});
}
else {
if constexpr (std::is_invocable_v<Visit, first_t, std::string_view>) {
visit_members(t, [&](auto&... args) {
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is]), ...);
}
(std::make_index_sequence<sizeof...(args)>{});
#else
visit_members_impl0<T>(std::forward<Visit>(func),
std::make_index_sequence<sizeof...(args)>{},
args...);
#endif
});
}
else if constexpr (std::is_invocable_v<Visit, first_t, std::string_view,
size_t>) {
visit_members(t, [&](auto&... args) {
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is], Is), ...);
}
(std::make_index_sequence<sizeof...(args)>{});
#else
visit_members_impl<T>(std::forward<Visit>(func),
std::make_index_sequence<sizeof...(args)>{},
args...);
#endif
});
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [field_value&, "
"std::string_view, size_t], at least has field_value and "
"make sure keep the order of arguments");
}
}
}
} // namespace ylt::reflection
#if (defined(__GNUC__) && __GNUC__ > 10) || \
((defined(__clang__) || defined(_MSC_VER)) && __has_include(<concepts>))
#if __cplusplus >= 202002L
template <ylt::reflection::FixedString s>
inline constexpr auto operator""_ylts() {
return s;
}
#endif
#endif