Skip to content

Commit

Permalink
[new lib]add reflection lib (#717)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Jul 12, 2024
1 parent ce86b8b commit d1fc7fa
Show file tree
Hide file tree
Showing 8 changed files with 1,729 additions and 1 deletion.
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at <http://www.boost.org/LICENSE_1_0.txt>)

===================================================================
The reflection of a struct and data format borrow various ideas from zpp_bits and msgpack.
The reflection of a struct and data format borrow various ideas from zpp_bits, reflect-cpp and msgpack.
coro_rpc borrow various ideas from rest_rpc.
141 changes: 141 additions & 0 deletions include/ylt/reflection/member_count.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

#pragma once
#include <optional>
#include <tuple>
#include <type_traits>
#include <vector>

namespace ylt::reflection {
namespace internal {
#if __cpp_concepts >= 201907L
template <typename Type>
concept tuple_size = requires(Type tuple) {
std::tuple_size<std::remove_cvref_t<Type>>::value;
};
#else
template <typename T, typename = void>
struct tuple_size_impl : std::false_type {};

template <typename T>
struct tuple_size_impl<
T, std::void_t<decltype(std::tuple_size<remove_cvref_t<T>>::value)>>
: std::true_type {};

template <typename T>
constexpr bool tuple_size = tuple_size_impl<T>::value;
#endif

#if __cpp_concepts >= 201907L
template <typename Type>
concept optional = requires(Type optional) {
optional.value();
optional.has_value();
optional.operator*();
typename std::remove_cvref_t<Type>::value_type;
};
#else
template <typename T, typename = void>
struct optional_impl : std::false_type {};

template <typename T>
struct optional_impl<T, std::void_t<decltype(std::declval<T>().value()),
decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().operator*()),
typename remove_cvref_t<T>::value_type>>
: std::true_type {};

template <typename T>
constexpr bool optional = !expected<T> && optional_impl<T>::value;
#endif

template <typename T, uint64_t version = 0>
struct compatible;

template <typename Type>
constexpr inline bool is_compatible_v = false;

template <typename Type, uint64_t version>
constexpr inline bool is_compatible_v<compatible<Type, version>> = true;

struct UniversalVectorType {
template <typename T>
operator std::vector<T>();
};

struct UniversalType {
template <typename T>
operator T();
};

struct UniversalIntegralType {
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
operator T();
};

struct UniversalNullptrType {
operator std::nullptr_t();
};

struct UniversalOptionalType {
template <typename U, typename = std::enable_if_t<optional<U>>>
operator U();
};

struct UniversalCompatibleType {
template <typename U, typename = std::enable_if_t<is_compatible_v<U>>>
operator U();
};

template <typename T, typename construct_param_t, typename = void,
typename... Args>
struct is_constructable_impl : std::false_type {};
template <typename T, typename construct_param_t, typename... Args>
struct is_constructable_impl<
T, construct_param_t,
std::void_t<decltype(T{{Args{}}..., {construct_param_t{}}})>, Args...>
: std::true_type {};

template <typename T, typename construct_param_t, typename... Args>
constexpr bool is_constructable =
is_constructable_impl<T, construct_param_t, void, Args...>::value;

template <typename T, typename... Args>
inline constexpr std::size_t members_count_impl() {
if constexpr (is_constructable<T, UniversalVectorType, Args...>) {
return members_count_impl<T, Args..., UniversalVectorType>();
}
else if constexpr (is_constructable<T, UniversalType, Args...>) {
return members_count_impl<T, Args..., UniversalType>();
}
else if constexpr (is_constructable<T, UniversalOptionalType, Args...>) {
return members_count_impl<T, Args..., UniversalOptionalType>();
}
else if constexpr (is_constructable<T, UniversalIntegralType, Args...>) {
return members_count_impl<T, Args..., UniversalIntegralType>();
}
else if constexpr (is_constructable<T, UniversalNullptrType, Args...>) {
return members_count_impl<T, Args..., UniversalNullptrType>();
}
else if constexpr (is_constructable<T, UniversalCompatibleType, Args...>) {
return members_count_impl<T, Args..., UniversalCompatibleType>();
}
else {
return sizeof...(Args);
}
}
} // namespace internal

template <typename T>
inline constexpr std::size_t members_count() {
using type = std::remove_cvref_t<T>;
if constexpr (internal::tuple_size<type>) {
return std::tuple_size<type>::value;
}
else {
return internal::members_count_impl<type>();
}
}

template <typename T>
constexpr std::size_t members_count_v = members_count<T>();
} // namespace ylt::reflection
174 changes: 174 additions & 0 deletions include/ylt/reflection/member_names.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#pragma once
#include <string_view>
#include <variant>

#include "template_string.hpp"
#if __has_include(<concepts>) || defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 10)
#include "member_ptr.hpp"

namespace ylt::reflection {

namespace internal {

template <class T>
struct Wrapper {
using Type = T;
T v;
};

template <class T>
Wrapper(T) -> Wrapper<T>;

// This workaround is necessary for clang.
template <class T>
inline constexpr auto wrap(const T& arg) noexcept {
return Wrapper{arg};
}

template <auto ptr>
inline constexpr std::string_view get_member_name() {
#if defined(_MSC_VER)
constexpr std::string_view func_name = __FUNCSIG__;
#else
constexpr std::string_view func_name = __PRETTY_FUNCTION__;
#endif

#if defined(__clang__)
auto split = func_name.substr(0, func_name.size() - 2);
return split.substr(split.find_last_of(":.") + 1);
#elif defined(__GNUC__)
auto split = func_name.substr(0, func_name.rfind(")}"));
return split.substr(split.find_last_of(":") + 1);
#elif defined(_MSC_VER)
auto split = func_name.substr(0, func_name.rfind("}>"));
return split.substr(split.rfind("->") + 2);
#else
static_assert(false,
"You are using an unsupported compiler. Please use GCC, Clang "
"or MSVC or switch to the rfl::Field-syntax.");
#endif
}
} // namespace internal

template <typename T>
inline constexpr std::array<std::string_view, members_count_v<T>>
get_member_names() {
constexpr size_t Count = members_count_v<T>;
constexpr auto tp = struct_to_tuple<T>();

std::array<std::string_view, Count> arr;
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
((arr[Is] = internal::get_member_name<internal::wrap(std::get<Is>(tp))>()),
...);
}
(std::make_index_sequence<Count>{});
return arr;
}

template <typename... Args>
inline constexpr auto tuple_to_variant(std::tuple<Args...>) {
return std::variant<std::add_pointer_t<Args>...>{};
}

template <typename T>
using struct_variant_t = decltype(tuple_to_variant(
std::declval<decltype(struct_to_tuple<std::remove_cvref_t<T>>())>));

template <typename T>
inline const auto& get_member_offset_arr() {
constexpr size_t Count = members_count_v<T>;
constexpr auto tp = struct_to_tuple<T>();

[[maybe_unused]] static std::array<size_t, Count> arr = {[&]<size_t... Is>(
std::index_sequence<Is...>) mutable {std::array<size_t, Count> arr;
((arr[Is] = size_t((const char*)std::get<Is>(tp) -
(char*)(&internal::wrapper<T>::value))),
...);
return arr;
}
(std::make_index_sequence<Count>{})
}; // namespace ylt::reflection

return arr;
}

template <typename T>
inline constexpr auto get_member_names_map() {
constexpr auto name_arr = get_member_names<T>();
return [&]<size_t... Is>(std::index_sequence<Is...>) mutable {
return frozen::unordered_map<frozen::string, size_t, name_arr.size()>{
{name_arr[Is], Is}...};
}
(std::make_index_sequence<name_arr.size()>{});
}

template <std::size_t N>
struct FixedString {
char data[N];
template <std::size_t... I>
constexpr FixedString(const char (&s)[N], std::index_sequence<I...>)
: data{s[I]...} {}
constexpr FixedString(const char (&s)[N])
: FixedString(s, std::make_index_sequence<N>()) {}
constexpr std::string_view str() const {
return std::string_view{data, N - 1};
}
};

template <typename T>
inline constexpr size_t index_of(std::string_view name) {
constexpr auto arr = get_member_names<T>();
for (size_t i = 0; i < arr.size(); i++) {
if (arr[i] == name) {
return i;
}
}

return arr.size();
}

template <typename T, FixedString name>
inline constexpr size_t index_of() {
return index_of<T>(name.str());
}

template <typename T, size_t index>
inline constexpr std::string_view name_of() {
static_assert(index < members_count_v<T>, "index out of range");
constexpr auto arr = get_member_names<T>();
return arr[index];
}

template <typename T>
inline constexpr std::string_view name_of(size_t index) {
constexpr auto arr = get_member_names<T>();
if (index >= arr.size()) {
return "";
}

return arr[index];
}

template <typename T, typename Visit>
inline constexpr void for_each(Visit func) {
constexpr auto arr = get_member_names<T>();
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
(func(arr[Is], Is), ...);
}
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
(func(arr[Is]), ...);
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [std::string_view, "
"size_t], at least has std::string_view and make sure keep "
"the order of arguments");
}
}
(std::make_index_sequence<arr.size()>{});
}

} // namespace ylt::reflection
#endif
Loading

0 comments on commit d1fc7fa

Please sign in to comment.