include/ylt/reflection/member_count.hpp (167 lines of code) (raw):
#pragma once
#include <cstdint>
#include <optional>
#include <tuple>
#include <type_traits>
#include <vector>
#if __has_include(<ylt/util/expected.hpp>)
#include <ylt/util/expected.hpp>
#else
#include "iguana/ylt/util/expected.hpp"
#endif
#include "user_reflect_macro.hpp"
namespace struct_pack {
template <typename T, uint64_t version>
struct compatible;
}
namespace ylt::reflection {
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
#if __cpp_concepts >= 201907L
template <typename Type>
concept expected = requires(Type e) {
typename remove_cvref_t<Type>::value_type;
typename remove_cvref_t<Type>::error_type;
typename remove_cvref_t<Type>::unexpected_type;
e.has_value();
e.error();
requires std::is_same_v<void, typename remove_cvref_t<Type>::value_type> ||
requires(Type e) {
e.value();
};
};
#else
template <typename T, typename = void>
struct expected_impl : std::false_type {};
template <typename T>
struct expected_impl<T, std::void_t<typename remove_cvref_t<T>::value_type,
typename remove_cvref_t<T>::error_type,
typename remove_cvref_t<T>::unexpected_type,
decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().error())>>
: std::true_type {};
// TODO: check e.value()
template <typename T>
constexpr bool expected = expected_impl<T>::value;
#endif
#if __cpp_concepts >= 201907L
template <typename Type>
concept optional = !expected<Type> && requires(Type optional) {
optional.value();
optional.has_value();
optional.operator*();
typename 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
namespace internal {
#if __cpp_concepts >= 201907L
template <typename Type>
concept tuple_size = requires(Type tuple) {
std::tuple_size<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
template <typename Type>
constexpr inline bool is_compatible_v = false;
template <typename Type, uint64_t version>
constexpr inline bool
is_compatible_v<struct_pack::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 = remove_cvref_t<T>;
if constexpr (is_out_ylt_refl_v<type>) {
return refl_member_count(ylt::reflection::identity<type>{});
}
else if constexpr (is_inner_ylt_refl_v<type>) {
return type::refl_member_count(ylt::reflection::identity<type>{});
}
else if constexpr (internal::tuple_size<type>) {
return std::tuple_size<type>::value;
}
else if constexpr (std::is_aggregate_v<type>) {
return internal::members_count_impl<type>();
}
else {
static_assert(!sizeof(T), "not supported type!");
}
}
template <typename T>
constexpr std::size_t members_count_v = members_count<T>();
} // namespace ylt::reflection