mirror of
https://github.com/qicosmos/rest_rpc.git
synced 2026-08-30 17:10:50 +08:00
991 lines
29 KiB
C++
991 lines
29 KiB
C++
/*
|
|
* Copyright (c) 2023, Alibaba Group Holding Limited;
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
// clang-format off
|
|
#pragma once
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
#include "ylt/reflection/member_count.hpp"
|
|
#include "ylt/reflection/internal/arg_list_macro.hpp"
|
|
|
|
#if __has_include(<span>)
|
|
#include <span>
|
|
#endif
|
|
|
|
#include "derived_helper.hpp"
|
|
#include "foreach_macro.h"
|
|
#include "marco.h"
|
|
#include "util.h"
|
|
#include "ylt/reflection/template_switch.hpp"
|
|
#include "ylt/reflection/member_ptr.hpp"
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
#include <concepts>
|
|
#endif
|
|
|
|
|
|
namespace struct_pack {
|
|
|
|
enum sp_config : uint64_t {
|
|
DEFAULT = 0,
|
|
DISABLE_TYPE_INFO = 0b1,
|
|
ENABLE_TYPE_INFO = 0b10,
|
|
DISABLE_ALL_META_INFO = 0b11,
|
|
ENCODING_WITH_VARINT = 0b100,
|
|
USE_FAST_VARINT = 0b1000
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template <typename... Args>
|
|
using get_args_type = remove_cvref_t<typename std::conditional<
|
|
sizeof...(Args) == 1, std::tuple_element_t<0, std::tuple<Args...>>,
|
|
std::tuple<Args...>>::type>;
|
|
|
|
template <typename U>
|
|
constexpr auto get_types();
|
|
|
|
template <typename T, template <typename, typename, std::size_t> typename Op,
|
|
typename... Contexts, std::size_t... I>
|
|
constexpr void for_each_impl(std::index_sequence<I...>, Contexts &...contexts) {
|
|
using type = decltype(get_types<T>());
|
|
(Op<T, std::tuple_element_t<I, type>, I>{}(contexts...), ...);
|
|
}
|
|
|
|
template <typename T, template <typename, typename, std::size_t> typename Op,
|
|
typename... Contexts>
|
|
constexpr void for_each(Contexts &...contexts) {
|
|
using type = decltype(get_types<T>());
|
|
for_each_impl<T, Op>(std::make_index_sequence<std::tuple_size_v<type>>(),
|
|
contexts...);
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr std::size_t members_count();
|
|
template <typename T>
|
|
constexpr std::size_t pack_align();
|
|
template <typename T>
|
|
constexpr std::size_t alignment();
|
|
} // namespace detail
|
|
|
|
template <typename T>
|
|
constexpr std::size_t members_count = detail::members_count<T>();
|
|
template <typename T>
|
|
constexpr std::size_t pack_alignment_v = 0;
|
|
template <typename T>
|
|
constexpr std::size_t alignment_v = 0;
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
|
|
template <typename T>
|
|
concept writer_t = requires(T t) {
|
|
t.write((const char *)nullptr, std::size_t{});
|
|
};
|
|
|
|
template <typename T>
|
|
concept reader_t = requires(T t) {
|
|
t.read((char *)nullptr, std::size_t{});
|
|
t.ignore(std::size_t{});
|
|
t.tellg();
|
|
};
|
|
|
|
template <typename T>
|
|
concept view_reader_t = reader_t<T> && requires(T t) {
|
|
{ t.read_view(std::size_t{}) } -> std::convertible_to<const char *>;
|
|
};
|
|
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct writer_t_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct writer_t_impl<T, std::void_t<decltype(std::declval<T>().write(
|
|
(const char *)nullptr, std::size_t{}))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool writer_t = writer_t_impl<T>::value;
|
|
|
|
template <typename T, typename = void>
|
|
struct reader_t_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct reader_t_impl<
|
|
T, std::void_t<decltype(std::declval<T>().read((char *)nullptr,
|
|
std::size_t{})),
|
|
decltype(std::declval<T>().ignore(std::size_t{})),
|
|
decltype(std::declval<T>().tellg())>> : std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool reader_t = reader_t_impl<T>::value;
|
|
|
|
template <typename T, typename = void>
|
|
struct view_reader_t_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct view_reader_t_impl<
|
|
T, std::void_t<decltype(std::declval<T>().read_view(std::size_t{}))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool view_reader_t = reader_t<T> &&view_reader_t_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
|
|
template <typename T>
|
|
concept can_reserve = requires(T t) {
|
|
t.reserve(std::size_t{});
|
|
};
|
|
|
|
template <typename T>
|
|
concept can_shrink_to_fit = requires(T t) {
|
|
t.shrink_to_fit();
|
|
};
|
|
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct can_reserve_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct can_reserve_impl<
|
|
T, std::void_t<decltype(std::declval<T>().reserve(std::size_t{}))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool can_reserve = can_reserve_impl<T>::value;
|
|
|
|
template <typename T, typename = void>
|
|
struct can_shrink_to_fit_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct can_shrink_to_fit_impl<
|
|
T, std::void_t<decltype(std::declval<T>().shrink_to_fit())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool can_shrink_to_fit = can_shrink_to_fit_impl<T>::value;
|
|
|
|
#endif
|
|
|
|
template <typename T, uint64_t version = 0>
|
|
struct compatible;
|
|
|
|
namespace detail {
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
|
|
template <typename T>
|
|
concept has_user_defined_id = requires {
|
|
typename std::integral_constant<std::size_t, T::struct_pack_id>;
|
|
};
|
|
|
|
template <typename T>
|
|
concept has_user_defined_id_ADL = requires {
|
|
typename std::integral_constant<std::size_t,
|
|
struct_pack_id((T*)nullptr)>;
|
|
};
|
|
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct has_user_defined_id_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct has_user_defined_id_impl<
|
|
T, std::void_t<std::integral_constant<std::size_t, T::struct_pack_id>>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool has_user_defined_id = has_user_defined_id_impl<T>::value;
|
|
|
|
template <std::size_t sz>
|
|
struct constant_checker{};
|
|
|
|
template <typename T, typename = void>
|
|
struct has_user_defined_id_ADL_impl : std::false_type {};
|
|
|
|
#ifdef _MSC_VER
|
|
// FIXME: we can't check if it's compile-time calculated in msvc with C++17
|
|
template <typename T>
|
|
struct has_user_defined_id_ADL_impl<
|
|
T, std::void_t<decltype(struct_pack_id((T*)nullptr))>>
|
|
: std::true_type {};
|
|
#else
|
|
|
|
template <typename T>
|
|
struct has_user_defined_id_ADL_impl<
|
|
T, std::void_t<constant_checker<struct_pack_id((T*)nullptr)>>>
|
|
: std::true_type {};
|
|
|
|
#endif
|
|
|
|
template <typename T>
|
|
constexpr bool has_user_defined_id_ADL = has_user_defined_id_ADL_impl<T>::value;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename T>
|
|
concept is_base_class = requires (T* t) {
|
|
std::is_same_v<uint32_t,decltype(t->get_struct_pack_id())>;
|
|
typename struct_pack::detail::derived_class_set_t<T>;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct is_base_class_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct is_base_class_impl<
|
|
T, std::void_t<
|
|
std::enable_if<std::is_same_v<decltype(((T*)nullptr)->get_struct_pack_id()), uint32_t>>,
|
|
typename struct_pack::detail::derived_class_set_t<T>>>
|
|
: std::true_type {};
|
|
template <typename T>
|
|
constexpr bool is_base_class=is_base_class_impl<T>::value;
|
|
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept deserialize_view = requires(Type container) {
|
|
container.size();
|
|
container.data();
|
|
};
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct deserialize_view_impl : std::false_type {};
|
|
template <typename T>
|
|
struct deserialize_view_impl<
|
|
T, std::void_t<decltype(std::declval<T>().size()),decltype(std::declval<T>().data())>>
|
|
: std::true_type {};
|
|
|
|
template <typename Type>
|
|
constexpr bool deserialize_view = deserialize_view_impl<Type>::value;
|
|
|
|
#endif
|
|
|
|
struct memory_writer {
|
|
char *buffer;
|
|
STRUCT_PACK_INLINE void write(const char *data, std::size_t len) {
|
|
memcpy(buffer, data, len);
|
|
buffer += len;
|
|
}
|
|
};
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept container_adapter = requires(Type container) {
|
|
typename remove_cvref_t<Type>::value_type;
|
|
container.size();
|
|
container.pop();
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct container_adapter_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct container_adapter_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::value_type,
|
|
decltype(std::declval<T>().size()),
|
|
decltype(std::declval<T>().pop())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool container_adapter = container_adapter_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept container = requires(Type container) {
|
|
typename remove_cvref_t<Type>::value_type;
|
|
container.size();
|
|
container.begin();
|
|
container.end();
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct container_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct container_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::value_type,
|
|
decltype(std::declval<T>().size()),
|
|
decltype(std::declval<T>().begin()),
|
|
decltype(std::declval<T>().end())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool container = container_impl<T>::value;
|
|
#endif
|
|
|
|
template <typename Type>
|
|
constexpr bool is_char_t = std::is_same_v<Type, signed char> ||
|
|
std::is_same_v<Type, char> || std::is_same_v<Type, unsigned char> ||
|
|
std::is_same_v<Type, wchar_t> || std::is_same_v<Type, char16_t> ||
|
|
std::is_same_v<Type, char32_t>
|
|
#ifdef __cpp_lib_char8_t
|
|
|| std::is_same_v<Type, char8_t>
|
|
#endif
|
|
;
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept string = container<Type> && requires(Type container) {
|
|
requires is_char_t<typename remove_cvref_t<Type>::value_type>;
|
|
container.length();
|
|
container.data();
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct string_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct string_impl<T, std::void_t<
|
|
std::enable_if_t<is_char_t<typename remove_cvref_t<T>::value_type>>,
|
|
decltype(std::declval<T>().length()),
|
|
decltype(std::declval<T>().data())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool string = string_impl<T>::value && container<T>;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept string_view = string<Type> && !requires(Type container) {
|
|
container.resize(std::size_t{});
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct string_view_impl : std::true_type {};
|
|
|
|
template <typename T>
|
|
struct string_view_impl<T, std::void_t<
|
|
decltype(std::declval<T>().resize(std::size_t{}))>>
|
|
: std::false_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool string_view = string<T> && string_view_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept span = container<Type> && requires(Type t) {
|
|
Type{(typename Type::value_type*)nullptr ,std::size_t{} };
|
|
t.subspan(std::size_t{},std::size_t{});
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct span_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct span_impl<T, std::void_t<
|
|
decltype(T{(typename T::value_type*)nullptr ,std::size_t{}}),
|
|
decltype(std::declval<T>().subspan(std::size_t{},std::size_t{}))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool span = container<T> && span_impl<T>::value;
|
|
#endif
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept dynamic_span = span<Type> && Type::extent == SIZE_MAX;
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct dynamic_span_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct dynamic_span_impl<T, std::void_t<
|
|
std::enable_if_t<(T::extent == SIZE_MAX)>>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool dynamic_span = span<T> && dynamic_span_impl<T>::value;
|
|
#endif
|
|
template <typename Type>
|
|
constexpr bool static_span = span<Type> && !dynamic_span<Type>;
|
|
|
|
|
|
#if __cpp_lib_span >= 202002L && __cpp_concepts>=201907L
|
|
|
|
template <typename Type>
|
|
concept continuous_container =
|
|
string<Type> || (container<Type> && requires(Type container) {
|
|
std::span{container};
|
|
});
|
|
|
|
#else
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_std_basic_string_v = false;
|
|
|
|
template <typename... args>
|
|
constexpr inline bool is_std_basic_string_v<std::basic_string<args...>> =
|
|
true;
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_std_vector_v = false;
|
|
|
|
template <typename... args>
|
|
constexpr inline bool is_std_vector_v<std::vector<args...>> = true;
|
|
|
|
template <typename Type>
|
|
constexpr bool continuous_container =
|
|
string<Type> || (container<Type> && (is_std_vector_v<Type> || is_std_basic_string_v<Type>));
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept map_container = container<Type> && requires(Type container) {
|
|
typename remove_cvref_t<Type>::mapped_type;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct map_container_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct map_container_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::mapped_type>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool map_container = container<T> && map_container_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept hash_map_container = map_container<Type> && requires(Type container) {
|
|
typename remove_cvref_t<Type>::hasher;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct hash_map_container_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct hash_map_container_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::hasher>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool hash_map_container = map_container<T> && hash_map_container_impl<T>::value;
|
|
#endif
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_std_unordered_map_v = false;
|
|
|
|
template <typename... args>
|
|
constexpr inline bool is_std_unordered_map_v<std::unordered_map<args...>> = true;
|
|
|
|
template <typename... args>
|
|
constexpr inline bool is_std_unordered_map_v<std::unordered_multimap<args...>> = true;
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept set_container = container<Type> && requires {
|
|
typename remove_cvref_t<Type>::key_type;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct set_container_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct set_container_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::key_type>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool set_container = container<T> && set_container_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept bitset = requires (Type t){
|
|
t.flip();
|
|
t.set();
|
|
t.reset();
|
|
t.count();
|
|
} && (Type{}.size()+7)/8 == sizeof(Type);
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct bitset_impl : std::false_type {};
|
|
|
|
|
|
template <typename T>
|
|
struct bitset_impl<T, std::void_t<
|
|
decltype(std::declval<T>().flip()),
|
|
decltype(std::declval<T>().set()),
|
|
decltype(std::declval<T>().reset()),
|
|
decltype(std::declval<T>().count()),
|
|
decltype(std::declval<T>().size())>>
|
|
: std::true_type {};
|
|
|
|
template<typename T>
|
|
constexpr bool bitset_size_checker() {
|
|
if constexpr (bitset_impl<T>::value) {
|
|
return (T{}.size()+7)/8==sizeof(T);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr bool bitset = bitset_impl<T>::value && bitset_size_checker<T>();
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept tuple = requires(Type tuple) {
|
|
std::get<0>(tuple);
|
|
sizeof(std::tuple_size<remove_cvref_t<Type>>);
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct tuple_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct tuple_impl<T, std::void_t<
|
|
decltype(std::get<0>(std::declval<T>())),
|
|
decltype(sizeof(std::tuple_size<remove_cvref_t<T>>::value))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool tuple = tuple_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept user_defined_refl = ylt::reflection::is_out_ylt_refl_v<Type> || ylt::reflection::is_inner_ylt_refl_v<Type>;
|
|
#else
|
|
template <typename T>
|
|
constexpr bool user_defined_refl = ylt::reflection::is_out_ylt_refl_v<T> || ylt::reflection::is_inner_ylt_refl_v<T>;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept user_defined_config_by_ADL = std::is_same_v<decltype(set_sp_config(std::declval<Type*>())),struct_pack::sp_config>;
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct user_defined_config_by_ADL_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct user_defined_config_by_ADL_impl<T, std::void_t<
|
|
std::enable_if_t<std::is_same_v<decltype(set_sp_config(std::declval<T*>())),struct_pack::sp_config>>>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool user_defined_config_by_ADL = user_defined_config_by_ADL_impl<T>::value;
|
|
#endif
|
|
|
|
template<typename T>
|
|
constexpr decltype(auto) delay_sp_config_eval() {
|
|
if constexpr (sizeof(T)==0) {
|
|
return (T*)nullptr;
|
|
}
|
|
else {
|
|
return (sp_config*)nullptr;
|
|
}
|
|
}
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template<typename T>
|
|
concept has_default_config = std::is_same_v<decltype(set_default(decltype(delay_sp_config_eval<T>()){})),struct_pack::sp_config>;
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct has_default_config_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct has_default_config_impl<T, std::void_t<
|
|
std::enable_if_t<std::is_same_v<decltype(set_default(decltype(delay_sp_config_eval<T>()){})),struct_pack::sp_config>>>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool has_default_config = has_default_config_impl<T>::value;
|
|
#endif
|
|
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept user_defined_config = requires {
|
|
Type::struct_pack_config;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct user_defined_config_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct user_defined_config_impl<T, std::void_t<decltype(T::struct_pack_config)>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool user_defined_config = user_defined_config_impl<T>::value;
|
|
#endif
|
|
|
|
struct memory_reader;
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept user_defined_serialization = requires (Type& t) {
|
|
sp_serialize_to(std::declval<struct_pack::detail::memory_writer&>(),(const Type&)t);
|
|
{sp_deserialize_to(std::declval<struct_pack::detail::memory_reader&>(),t)} -> std::same_as<struct_pack::err_code>;
|
|
{sp_get_needed_size((const Type&)t)}->std::same_as<std::size_t>;
|
|
};
|
|
template <typename Type>
|
|
concept user_defined_type_name = requires {
|
|
{ sp_set_type_name((Type*)nullptr) } -> std::same_as<std::string_view>;
|
|
};
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct user_defined_serialization_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct user_defined_serialization_impl<T, std::void_t<
|
|
decltype(sp_serialize_to(std::declval<struct_pack::detail::memory_writer&>(),std::declval<const T&>())),
|
|
std::enable_if<std::is_same_v<decltype(sp_deserialize_to(std::declval<struct_pack::detail::memory_reader&>(),std::declval<T&>())), struct_pack::err_code>,
|
|
std::enable_if<std::is_same_v<decltype(sp_get_needed_size(std::declval<const T&>())), std::string_view>>>>>
|
|
: std::true_type {};
|
|
|
|
template <typename Type>
|
|
constexpr bool user_defined_serialization = user_defined_serialization_impl<Type>::value;
|
|
|
|
template <typename T, typename = void>
|
|
struct user_defined_type_name_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct user_defined_type_name_impl<T, std::void_t<
|
|
std::enable_if<std::is_same_v<decltype(sp_set_type_name((T*)nullptr)), std::string_view>>>>
|
|
: std::true_type {};
|
|
|
|
template <typename Type>
|
|
constexpr bool user_defined_type_name = user_defined_type_name_impl<Type>::value;
|
|
#endif
|
|
|
|
#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
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept array = requires(Type arr) {
|
|
arr.size();
|
|
std::tuple_size<remove_cvref_t<Type>>{};
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct array_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct array_impl<T, std::void_t<
|
|
decltype(std::declval<T>().size()),
|
|
decltype(std::tuple_size<remove_cvref_t<T>>{})>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool array = array_impl<T>::value;
|
|
#endif
|
|
|
|
|
|
template <class T>
|
|
constexpr bool c_array =
|
|
std::is_array_v<T> && std::extent_v<remove_cvref_t<T>> > 0;
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept pair = requires(Type p) {
|
|
typename remove_cvref_t<Type>::first_type;
|
|
typename remove_cvref_t<Type>::second_type;
|
|
p.first;
|
|
p.second;
|
|
};
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct pair_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct pair_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::first_type,
|
|
typename remove_cvref_t<T>::second_type,
|
|
decltype(std::declval<T>().first),
|
|
decltype(std::declval<T>().second)>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool pair = pair_impl<T>::value;
|
|
#endif
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
template <typename Type>
|
|
concept unique_ptr = requires(Type ptr) {
|
|
ptr.operator*();
|
|
typename remove_cvref_t<Type>::element_type;
|
|
}
|
|
&&!requires(Type ptr, Type ptr2) { ptr = ptr2; };
|
|
#else
|
|
template <typename T, typename = void>
|
|
struct unique_ptr_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct unique_ptr_impl<T, std::void_t<
|
|
typename remove_cvref_t<T>::element_type,
|
|
decltype(std::declval<T>().operator*())>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool unique_ptr = unique_ptr_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<compatible<Type,version>> = true;
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_variant_v = false;
|
|
|
|
template <typename... args>
|
|
constexpr inline bool is_variant_v<std::variant<args...>> = true;
|
|
|
|
template <typename T>
|
|
constexpr inline bool is_trivial_tuple = false;
|
|
|
|
template <typename T>
|
|
class varint;
|
|
|
|
template <typename T>
|
|
class sint;
|
|
|
|
template <typename T>
|
|
constexpr bool varintable_t =
|
|
std::is_same_v<T, varint<int32_t>> || std::is_same_v<T, varint<int64_t>> ||
|
|
std::is_same_v<T, varint<uint32_t>> || std::is_same_v<T, varint<uint64_t>>;
|
|
template <typename T>
|
|
constexpr bool sintable_t =
|
|
std::is_same_v<T, sint<int32_t>> || std::is_same_v<T, sint<int64_t>>;
|
|
|
|
template <typename T, uint64_t parent_tag = 0 >
|
|
constexpr bool varint_t = varintable_t<T> || sintable_t<T> || ((parent_tag&struct_pack::ENCODING_WITH_VARINT) && (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T,int64_t> || std::is_same_v<T,uint64_t>));
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_trivial_view_v = false;
|
|
|
|
template <typename... Args>
|
|
constexpr uint64_t get_parent_tag();
|
|
|
|
template <typename T, bool ignore_compatible_field = false, uint64_t parent_tag = 0>
|
|
struct is_trivial_serializable {
|
|
private:
|
|
template<typename U,uint64_t parent_tag_=0, std::size_t... I>
|
|
static constexpr bool class_visit_helper(std::index_sequence<I...>) {
|
|
return (is_trivial_serializable<std::tuple_element_t<I, U>,
|
|
ignore_compatible_field,parent_tag_>::value &&
|
|
...);
|
|
}
|
|
|
|
public:
|
|
static constexpr bool solve() {
|
|
if constexpr (user_defined_serialization<T>) {
|
|
return false;
|
|
}
|
|
else if constexpr (std::is_same_v<T,std::monostate>) {
|
|
return true;
|
|
}
|
|
else if constexpr (std::is_abstract_v<T>) {
|
|
return false;
|
|
}
|
|
else if constexpr (varint_t<T,parent_tag>) {
|
|
return false;
|
|
}
|
|
else if constexpr (is_compatible_v<T> || is_trivial_view_v<T>) {
|
|
return ignore_compatible_field;
|
|
}
|
|
else if constexpr (std::is_enum_v<T> || std::is_fundamental_v<T> || bitset<T>
|
|
#if (__GNUC__ || __clang__) && defined(STRUCT_PACK_ENABLE_INT128)
|
|
|| std::is_same_v<__int128,T> || std::is_same_v<unsigned __int128,T>
|
|
#endif
|
|
) {
|
|
return true;
|
|
}
|
|
else if constexpr (array<T>) {
|
|
return is_trivial_serializable<typename T::value_type,
|
|
ignore_compatible_field>::value;
|
|
}
|
|
else if constexpr (c_array<T>) {
|
|
return is_trivial_serializable<typename std::remove_all_extents<T>::type,
|
|
ignore_compatible_field>::value;
|
|
}
|
|
else if constexpr (!pair<T> && tuple<T> && !is_trivial_tuple<T>) {
|
|
return false;
|
|
}
|
|
else if constexpr (user_defined_refl<T>) {
|
|
return false;
|
|
}
|
|
else if constexpr (container<T> || ylt::reflection::optional<T> || is_variant_v<T> ||
|
|
unique_ptr<T> || ylt::reflection::expected<T> || container_adapter<T>) {
|
|
return false;
|
|
}
|
|
else if constexpr (pair<T>) {
|
|
return is_trivial_serializable<typename T::first_type,
|
|
ignore_compatible_field>::value &&
|
|
is_trivial_serializable<typename T::second_type,
|
|
ignore_compatible_field>::value;
|
|
}
|
|
else if constexpr (is_trivial_tuple<T>) {
|
|
return class_visit_helper<T>(std::make_index_sequence<std::tuple_size_v<T>>{});
|
|
}
|
|
else if constexpr (std::is_class_v<T>) {
|
|
constexpr auto tag = get_parent_tag<T>();
|
|
using U = decltype(get_types<T>());
|
|
return class_visit_helper<U , tag>(std::make_index_sequence<std::tuple_size_v<U>>{});
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static inline constexpr bool value = is_trivial_serializable<std::remove_cv_t<T>,ignore_compatible_field,parent_tag>::solve();
|
|
};
|
|
|
|
}
|
|
template <typename T, typename = std::enable_if_t<detail::is_trivial_serializable<T>::value>>
|
|
struct trivial_view;
|
|
namespace detail {
|
|
|
|
#if __cpp_concepts < 201907L
|
|
|
|
template <typename T, typename = void>
|
|
struct trivially_copyable_container_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct trivially_copyable_container_impl<T, std::void_t<std::enable_if_t<is_trivial_serializable<typename T::value_type>::value>>>
|
|
: std::true_type {};
|
|
|
|
template <typename Type>
|
|
constexpr bool trivially_copyable_container =
|
|
continuous_container<Type> && trivially_copyable_container_impl<Type>::value;
|
|
|
|
#else
|
|
|
|
template <typename Type>
|
|
constexpr bool trivially_copyable_container =
|
|
continuous_container<Type> &&
|
|
requires(Type container) {
|
|
requires is_trivial_serializable<typename Type::value_type>::value;
|
|
};
|
|
|
|
#endif
|
|
|
|
template <typename Type>
|
|
constexpr inline bool is_trivial_view_v<struct_pack::trivial_view<Type>> = true;
|
|
|
|
template <typename T>
|
|
constexpr std::size_t members_count() {
|
|
return ylt::reflection::members_count_v<T>;
|
|
}
|
|
|
|
template<typename Object,typename Visitor>
|
|
constexpr decltype(auto) STRUCT_PACK_INLINE visit_members(Object &&object,
|
|
Visitor &&visitor) {
|
|
using type = remove_cvref_t<decltype(object)>;
|
|
constexpr auto Count = struct_pack::members_count<type>;
|
|
if constexpr (Count == 0 && std::is_class_v<type> &&
|
|
!std::is_same_v<type, std::monostate>) {
|
|
static_assert(!sizeof(type), "1. If the struct is empty, which is not allowed in struct_pack type system.\n"
|
|
"2. If the strut is not empty, it means struct_pack can't calculate your struct members' count. You can use macro YLT_REFL(Typename, field1, field2...).");
|
|
}
|
|
// If you see any structured binding error in the follow line, it
|
|
// means struct_pack can't calculate your struct's members count
|
|
// correctly.
|
|
// The best way is use macro YLT_REFL(Typename, field1, field2...)
|
|
// See the src/struct_pack/example/non_aggregated_type.cpp for more details.
|
|
//
|
|
// You can also to mark it manually.
|
|
// For example, there is a struct named Hello,
|
|
// and it has 3 members.
|
|
//
|
|
// You can mark it as:
|
|
//
|
|
// template <>
|
|
// constexpr size_t struct_pack::members_count<Hello> = 3;
|
|
|
|
return ylt::reflection::visit_members<Object, Visitor, Count>(std::forward<Object>(object), std::forward<Visitor>(visitor));
|
|
}
|
|
} // namespace detail
|
|
#if __cpp_concepts >= 201907L
|
|
|
|
template <typename T>
|
|
concept checkable_reader_t = reader_t<T> && requires(T t) {
|
|
t.check(std::size_t{});
|
|
};
|
|
|
|
#else
|
|
|
|
template <typename T, typename = void>
|
|
struct checkable_reader_t_impl : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct checkable_reader_t_impl<
|
|
T, std::void_t<decltype(std::declval<T>().check(std::size_t{}))>>
|
|
: std::true_type {};
|
|
|
|
template <typename T>
|
|
constexpr bool checkable_reader_t = reader_t<T> &&checkable_reader_t_impl<T>::value;
|
|
#endif
|
|
} // namespace struct_pack
|
|
|
|
// This marco is only for compatible with old version. Please instead it by YLT_REFL
|
|
#define STRUCT_PACK_REFL(Type,...) \
|
|
YLT_REFL(Type,__VA_ARGS__)
|