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()
28 -> decltype(static_cast<void>(sizeof(T)), false) {
29 return std::is_aggregate_v<T>;
30}
31
32template <typename /*T*/, typename... Args>
33constexpr auto IsDefinedAndAggregate(Args...) -> bool {
34 return false;
35}
36
37} // namespace utils::impl
38
39USERVER_NAMESPACE_END
40
41/// @cond
42
43// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
44#define USERVER_IMPL_STRUCT_MAP(r, data, elem)
45 std::forward<OtherDeps>(other).elem,
46
47// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
48#define USERVER_IMPL_MAKE_FROM_SUPERSET(Self, ...)
49 template <typename OtherDeps>
50 static Self MakeFromSupersetImpl(
51 OtherDeps&& other, USERVER_NAMESPACE::utils::impl::InternalTag) {
52 return {BOOST_PP_SEQ_FOR_EACH(USERVER_IMPL_STRUCT_MAP, BOOST_PP_EMPTY(),
53 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__))};
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_MAKE_STRUCT_SUBSET and @ref USERVER_MAKE_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<
78 USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int>
79 Enable = 0>
80 /*implicit*/ operator Other() const& {
81 return Other::MakeFromSupersetImpl(
82 *this, USERVER_NAMESPACE::utils::impl::InternalTag{});
83 }
84
85 template <
86 typename Other,
87 std::enable_if_t<
88 USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>(), int>
89 Enable = 0>
90 /*implicit*/ operator Other()&& {
91 return Other::MakeFromSupersetImpl(
92 std::move(*this), USERVER_NAMESPACE::utils::impl::InternalTag{});
93 }
94
95 friend struct USERVER_NAMESPACE::utils::impl::RequireSemicolon
96
97/// @brief Defines a struct containing a subset of data members
98/// from @a OriginalDependencies.
99///
100/// Implicit conversions (by copy and by move) are allowed from any superset
101/// struct to the @a SubsetStruct, as long as the names of the data members
102/// match, and the superset struct is either defined using
103/// @ref USERVER_MAKE_STRUCT_SUBSET or @ref USERVER_MAKE_STRUCT_SUBSET_REF,
104/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
105///
106/// Usage example:
107/// @snippet utils/struct_subsets_test.cpp deps definitions
108/// @snippet utils/struct_subsets_test.cpp deps usage
109///
110/// @param SubsetStruct the name of the subset struct to define
111/// @param OriginalStruct the name of the superset struct, including its
112/// namespace if needed
113/// @param ... names of the data members to copy
114///
115/// @hideinitializer
116// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
117#define USERVER_DEFINE_STRUCT_SUBSET(SubsetStruct, OriginalStruct, ...)
118 struct SubsetStruct {
119 BOOST_PP_SEQ_FOR_EACH(USERVER_IMPL_STRUCT_SUBSET_MAP, OriginalStruct,
120 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__))
121
122 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStruct, __VA_ARGS__)
123
125 }
126
127/// @brief Defines a struct containing a subset of data members
128/// from @a OriginalDependencies. Appends `const&` to types of all non-reference
129/// data members.
130///
131/// Implicit conversions (by copy and by move) are allowed from any superset
132/// struct to the @a SubsetStruct, as long as the names of the data members
133/// match, and the superset struct is either defined using
134/// @ref USERVER_MAKE_STRUCT_SUBSET or @ref USERVER_MAKE_STRUCT_SUBSET_REF,
135/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
136///
137/// `*Ref` structs can be used for parameters of utility functions to avoid
138/// copying non-reference data members.
139///
140/// Usage example:
141/// @snippet utils/struct_subsets_test.cpp ref definitions
142/// @snippet utils/struct_subsets_test.cpp ref usage
143///
144/// @param SubsetStructRef the name of the subset struct to define, it should
145/// typically contain `*Ref` suffix to underline that it needs the original
146/// backing struct to work
147/// @param OriginalStruct the name of the superset struct, including its
148/// namespace if needed
149/// @param ... names of the data members to copy
150///
151/// @hideinitializer
152// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
153#define USERVER_DEFINE_STRUCT_SUBSET_REF(SubsetStructRef, OriginalStruct, ...)
154 struct SubsetStructRef {
155 BOOST_PP_SEQ_FOR_EACH(USERVER_IMPL_STRUCT_SUBSET_REF_MAP, OriginalStruct,
156 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__))
157
158 /* Protects against copying into async functions */
159 USERVER_NAMESPACE::utils::impl::NonMovable _impl_non_movable{
160 USERVER_NAMESPACE::utils::impl::InternalTag{}};
161
162 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStructRef, __VA_ARGS__)
163
165 }