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

[new lib]add reflection lib #717

Merged
merged 5 commits into from
Jul 12, 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
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
Loading