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,
51 BOOST_PP_EMPTY(),
52 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
53 )};
54 }
55
56// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
57#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem)
58 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
59 decltype(data::elem) elem;
60
61// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
62#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem)
63 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
64 std::add_const_t<decltype(data::elem)>& elem;
65
66/// @endcond
67
68/// @brief Should be invoked inside a manually defined "root" struct to enable
69/// conversions from it to subset structs created by
70/// @ref USERVER_DEFINE_STRUCT_SUBSET and @ref USERVER_DEFINE_STRUCT_SUBSET_REF.
71///
72/// @hideinitializer
73// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
74#define USERVER_ALLOW_CONVERSIONS_TO_SUBSET()
75 template <
76 typename Other,
77 std::enable_if_t<USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int> Enable = 0>
78 /*implicit*/ operator Other() const& {
79 return Other::MakeFromSupersetImpl(*this, USERVER_NAMESPACE::utils::impl::InternalTag{});
80 }
81
82 template <
83 typename Other,
84 std::enable_if_t<USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int> Enable = 0>
85 /*implicit*/ operator Other()&& {
86 return Other::MakeFromSupersetImpl(std::move(*this), USERVER_NAMESPACE::utils::impl::InternalTag{});
87 }
88
89 friend struct USERVER_NAMESPACE::utils::impl::RequireSemicolon
90
91/// @brief Defines a struct containing a subset of data members
92/// from @a OriginalDependencies.
93///
94/// Implicit conversions (by copy and by move) are allowed from any superset
95/// struct to the @a SubsetStruct, as long as the names of the data members
96/// match, and the superset struct is either defined using
97/// @ref USERVER_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_STRUCT_SUBSET_REF,
98/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
99///
100/// Usage example:
101/// @snippet utils/struct_subsets_test.cpp deps definitions
102/// @snippet utils/struct_subsets_test.cpp deps usage
103///
104/// @param SubsetStruct the name of the subset struct to define
105/// @param OriginalStruct the name of the superset struct, including its
106/// namespace if needed
107/// @param ... names of the data members to copy
108///
109/// @hideinitializer
110// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
111#define USERVER_DEFINE_STRUCT_SUBSET(SubsetStruct, OriginalStruct, ...)
112 struct SubsetStruct {
113 BOOST_PP_SEQ_FOR_EACH(
114 USERVER_IMPL_STRUCT_SUBSET_MAP,
115 OriginalStruct,
116 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
117 )
118
119 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStruct, __VA_ARGS__)
120
122 }
123
124/// @brief Defines a struct containing a subset of data members
125/// from @a OriginalDependencies. Appends `const&` to types of all non-reference
126/// data members.
127///
128/// Implicit conversions (by copy and by move) are allowed from any superset
129/// struct to the @a SubsetStruct, as long as the names of the data members
130/// match, and the superset struct is either defined using
131/// @ref USERVER_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_STRUCT_SUBSET_REF,
132/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
133///
134/// `*Ref` structs can be used for parameters of utility functions to avoid
135/// copying non-reference data members.
136///
137/// Usage example:
138/// @snippet utils/struct_subsets_test.cpp ref definitions
139/// @snippet utils/struct_subsets_test.cpp ref usage
140///
141/// @param SubsetStructRef the name of the subset struct to define, it should
142/// typically contain `*Ref` suffix to underline that it needs the original
143/// backing struct to work
144/// @param OriginalStruct the name of the superset struct, including its
145/// namespace if needed
146/// @param ... names of the data members to copy
147///
148/// @hideinitializer
149// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
150#define USERVER_DEFINE_STRUCT_SUBSET_REF(SubsetStructRef, OriginalStruct, ...)
151 struct SubsetStructRef {
152 BOOST_PP_SEQ_FOR_EACH(
153 USERVER_IMPL_STRUCT_SUBSET_REF_MAP,
154 OriginalStruct,
155 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
156 )
157
158 /* Protects against copying into async functions */
159 USERVER_NAMESPACE::utils::impl::NonMovable _impl_non_movable{USERVER_NAMESPACE::utils::impl::InternalTag{}};
160
161 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStructRef, __VA_ARGS__)
162
164 }