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/forward_like.hpp>
14#include <userver/utils/impl/boost_variadic_to_seq.hpp>
15#include <userver/utils/impl/internal_tag.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace utils::impl {
20
21struct RequireSemicolon;
22
23struct NonMovable final {
24 constexpr explicit NonMovable(InternalTag) noexcept {}
25};
26
27template <typename T>
28constexpr auto IsDefinedAndAggregate() -> 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
37template <typename TOwner, typename TMember>
38USERVER_IMPL_NODEBUG_INLINE_FUNC decltype(auto) ForwardLikeExplicit(TMember& member) noexcept {
39 if constexpr (std::is_lvalue_reference_v<TOwner> || std::is_lvalue_reference_v<TMember>) {
40 return member;
41 } else {
42 return std::move(member);
43 }
44}
45
46template <typename TOwner, typename TMember>
47USERVER_IMPL_NODEBUG_INLINE_FUNC decltype(auto) ForwardLikeExplicit(const TMember& member) noexcept {
48 return member;
49}
50
51} // namespace utils::impl
52
53USERVER_NAMESPACE_END
54
55/// @cond
56
57// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
58#define USERVER_IMPL_STRUCT_MAP(r, data, elem)
59 USERVER_NAMESPACE::utils::impl::ForwardLikeExplicit<OtherDeps, decltype(other.elem)>(other.elem),
60
61// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
62#define USERVER_IMPL_MAKE_FROM_SUPERSET(Self, ...)
63 template <typename OtherDeps>
64 static Self MakeFromSupersetImpl(OtherDeps&& other, USERVER_NAMESPACE::utils::impl::InternalTag) {
65 return {BOOST_PP_SEQ_FOR_EACH(
66 USERVER_IMPL_STRUCT_MAP,
67 BOOST_PP_EMPTY(),
68 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
69 )};
70 }
71
72// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
73#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem)
74 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
75 decltype(data::elem) elem;
76
77// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
78#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem)
79 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
80 std::add_const_t<decltype(data::elem)>& elem;
81
82/// @endcond
83
84/// @brief Should be invoked inside a manually defined "root" struct to enable
85/// conversions from it to subset structs created by
86/// @ref USERVER_DEFINE_STRUCT_SUBSET and @ref USERVER_DEFINE_STRUCT_SUBSET_REF.
87///
88/// @hideinitializer
89// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
90#define USERVER_ALLOW_CONVERSIONS_TO_SUBSET()
91 template <typename Other>
92 requires(USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>()) /*implicit*/ operator Other() const& {
93 return Other::MakeFromSupersetImpl(*this, USERVER_NAMESPACE::utils::impl::InternalTag{});
94 }
95
96 template <typename Other>
97 requires(USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>()) /*implicit*/ operator Other()&& {
98 return Other::MakeFromSupersetImpl(std::move(*this), USERVER_NAMESPACE::utils::impl::InternalTag{});
99 }
100
101 friend struct USERVER_NAMESPACE::utils::impl::RequireSemicolon
102
103/// @brief Defines a struct containing a subset of data members
104/// from @a OriginalDependencies.
105///
106/// Implicit conversions (by copy and by move) are allowed from any superset
107/// struct to the @a SubsetStruct, as long as the names of the data members
108/// match, and the superset struct is either defined using
109/// @ref USERVER_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_STRUCT_SUBSET_REF,
110/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
111///
112/// Usage example:
113/// @snippet utils/struct_subsets_test.cpp deps definitions
114/// @snippet utils/struct_subsets_test.cpp deps usage
115///
116/// @param SubsetStruct the name of the subset struct to define
117/// @param OriginalStruct the name of the superset struct, including its
118/// namespace if needed
119/// @param ... names of the data members to copy
120///
121/// @hideinitializer
122// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
123#define USERVER_DEFINE_STRUCT_SUBSET(SubsetStruct, OriginalStruct, ...)
124 struct SubsetStruct {
125 BOOST_PP_SEQ_FOR_EACH(
126 USERVER_IMPL_STRUCT_SUBSET_MAP,
127 OriginalStruct,
128 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
129 )
130
131 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStruct, __VA_ARGS__)
132
134 }
135
136/// @brief Defines a struct containing a subset of data members
137/// from @a OriginalDependencies. Appends `const&` to types of all non-reference
138/// data members.
139///
140/// Implicit conversions (by copy and by move) are allowed from any superset
141/// struct to the @a SubsetStruct, as long as the names of the data members
142/// match, and the superset struct is either defined using
143/// @ref USERVER_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_STRUCT_SUBSET_REF,
144/// or it contains @ref USERVER_ALLOW_CONVERSIONS_TO_SUBSET.
145///
146/// `*Ref` structs can be used for parameters of utility functions to avoid
147/// copying non-reference data members.
148///
149/// Usage example:
150/// @snippet utils/struct_subsets_test.cpp ref definitions
151/// @snippet utils/struct_subsets_test.cpp ref usage
152///
153/// @param SubsetStructRef the name of the subset struct to define, it should
154/// typically contain `*Ref` suffix to underline that it needs the original
155/// backing struct to work
156/// @param OriginalStruct the name of the superset struct, including its
157/// namespace if needed
158/// @param ... names of the data members to copy
159///
160/// @hideinitializer
161// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
162#define USERVER_DEFINE_STRUCT_SUBSET_REF(SubsetStructRef, OriginalStruct, ...)
163 struct SubsetStructRef {
164 BOOST_PP_SEQ_FOR_EACH(
165 USERVER_IMPL_STRUCT_SUBSET_REF_MAP,
166 OriginalStruct,
167 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
168 )
169
170 /* Protects against copying into async functions */
171 USERVER_NAMESPACE::utils::impl::NonMovable _impl_non_movable{USERVER_NAMESPACE::utils::impl::InternalTag{}};
172
173 USERVER_IMPL_MAKE_FROM_SUPERSET(SubsetStructRef, __VA_ARGS__)
174
176 }