userver: userver/utils/struct_subsets.hpp Source File
Loading...
Searching...
No Matches
struct_subsets.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/struct_subsets.hpp
4/// @brief Utilities for creating a struct with a subset of fields of the
5/// original struct, with conversions between them.
6
7#include <type_traits>
8#include <utility>
9
10#include <boost/preprocessor/empty.hpp>
11#include <boost/preprocessor/seq/for_each.hpp>
12
13#include <userver/utils/impl/boost_variadic_to_seq.hpp>
14#include <userver/utils/impl/internal_tag.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace utils::impl {
19
20struct RequireSemicolon;
21
22struct NonMovable final {
23 constexpr explicit NonMovable(InternalTag) noexcept {}
24};
25
26template <typename T>
27constexpr auto IsDefinedAndAggregate() -> decltype(static_cast<void>(sizeof(T)), false) {
28 return std::is_aggregate_v<T>;
29}
30
31template <typename /*T*/, typename... Args>
32constexpr auto IsDefinedAndAggregate(Args...) -> bool {
33 return false;
34}
35
36} // namespace utils::impl
37
38USERVER_NAMESPACE_END
39
40/// @cond
41
42// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
43#define USERVER_IMPL_STRUCT_MAP(r, data, elem) std::forward<OtherDeps>(other).elem,
44
45// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
46#define USERVER_IMPL_MAKE_FROM_SUPERSET(Self, ...)
47 template <typename OtherDeps>
48 static Self MakeFromSupersetImpl(OtherDeps&& other, USERVER_NAMESPACE::utils::impl::InternalTag) {
49 return {BOOST_PP_SEQ_FOR_EACH(
50 USERVER_IMPL_STRUCT_MAP, BOOST_PP_EMPTY(), USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
51 )};
52 }
53
54// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
55#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem)
56 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
57 decltype(data::elem) elem;
58
59// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
60#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem)
61 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
62 std::add_const_t<decltype(data::elem)>& elem;
63
64/// @endcond
65
66/// @brief Should be invoked inside a manually defined "root" struct to enable
67/// conversions from it to subset structs created by
68/// @ref USERVER_MAKE_STRUCT_SUBSET and @ref USERVER_MAKE_STRUCT_SUBSET_REF.
69///
70/// @hideinitializer
71// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
72#define USERVER_ALLOW_CONVERSIONS_TO_SUBSET()
73 template <
74 typename Other,
75 std::enable_if_t<USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int> Enable = 0>
76 /*implicit*/ operator Other() const& {
77 return Other::MakeFromSupersetImpl(*this, USERVER_NAMESPACE::utils::impl::InternalTag{});
78 }
79
80 template <
81 typename Other,
82 std::enable_if_t<USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int> Enable = 0>
83 /*implicit*/ operator Other()&& {
84 return Other::MakeFromSupersetImpl(std::move(*this), USERVER_NAMESPACE::utils::impl::InternalTag{});
85 }
86
87 friend struct USERVER_NAMESPACE::utils::impl::RequireSemicolon
88
89/// @brief Defines a struct containing a subset of data members
90/// from @a OriginalDependencies.
91///
92/// Implicit conversions (by copy and by move) are allowed from any superset
93/// struct to the @a SubsetStruct, as long as the names of the data members
94/// match, and the superset struct is either defined using
95/// @ref USERVER_MAKE_STRUCT_SUBSET or @ref USERVER_MAKE_STRUCT_SUBSET_REF,
96/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
97///
98/// Usage example:
99/// @snippet utils/struct_subsets_test.cpp deps definitions
100/// @snippet utils/struct_subsets_test.cpp deps usage
101///
102/// @param SubsetStruct the name of the subset struct to define
103/// @param OriginalStruct the name of the superset struct, including its
104/// namespace if needed
105/// @param ... names of the data members to copy
106///
107/// @hideinitializer
108// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
109#define USERVER_DEFINE_STRUCT_SUBSET(SubsetStruct, OriginalStruct, ...)
110 struct SubsetStruct {
111 BOOST_PP_SEQ_FOR_EACH(
112 USERVER_IMPL_STRUCT_SUBSET_MAP,
113 OriginalStruct,
114 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
115 )
116
117 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStruct, __VA_ARGS__)
118
120 }
121
122/// @brief Defines a struct containing a subset of data members
123/// from @a OriginalDependencies. Appends `const&` to types of all non-reference
124/// data members.
125///
126/// Implicit conversions (by copy and by move) are allowed from any superset
127/// struct to the @a SubsetStruct, as long as the names of the data members
128/// match, and the superset struct is either defined using
129/// @ref USERVER_MAKE_STRUCT_SUBSET or @ref USERVER_MAKE_STRUCT_SUBSET_REF,
130/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
131///
132/// `*Ref` structs can be used for parameters of utility functions to avoid
133/// copying non-reference data members.
134///
135/// Usage example:
136/// @snippet utils/struct_subsets_test.cpp ref definitions
137/// @snippet utils/struct_subsets_test.cpp ref usage
138///
139/// @param SubsetStructRef the name of the subset struct to define, it should
140/// typically contain `*Ref` suffix to underline that it needs the original
141/// backing struct to work
142/// @param OriginalStruct the name of the superset struct, including its
143/// namespace if needed
144/// @param ... names of the data members to copy
145///
146/// @hideinitializer
147// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
148#define USERVER_DEFINE_STRUCT_SUBSET_REF(SubsetStructRef, OriginalStruct, ...)
149 struct SubsetStructRef {
150 BOOST_PP_SEQ_FOR_EACH(
151 USERVER_IMPL_STRUCT_SUBSET_REF_MAP,
152 OriginalStruct,
153 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
154 )
155
156 /* Protects against copying into async functions */
157 USERVER_NAMESPACE::utils::impl::NonMovable _impl_non_movable{USERVER_NAMESPACE::utils::impl::InternalTag{}};
158
159 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStructRef, __VA_ARGS__)
160
162 }