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
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 USERVER_NAMESPACE::utils::ForwardLike<OtherDeps, decltype(other.elem)>(other.elem),
46
47// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
48#define USERVER_IMPL_MAKE_FROM_SUPERSET(Self, ...)
49 template <typename OtherDeps>
50 static Self MakeFromSupersetImpl(OtherDeps&& other, USERVER_NAMESPACE::utils::impl::InternalTag) {
51 return {BOOST_PP_SEQ_FOR_EACH(
52 USERVER_IMPL_STRUCT_MAP,
53 BOOST_PP_EMPTY(),
54 USERVER_IMPL_VARIADIC_TO_SEQ(__VA_ARGS__)
55 )};
56 }
57
58// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
59#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem)
60 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
61 decltype(data::elem) elem;
62
63// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
64#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem)
65 /* NOLINTNEXTLINE(bugprone-macro-parentheses) */
66 std::add_const_t<decltype(data::elem)>& elem;
67
68/// @endcond
69
70/// @brief Should be invoked inside a manually defined "root" struct to enable
71/// conversions from it to subset structs created by
72/// @ref USERVER_DEFINE_STRUCT_SUBSET and @ref USERVER_DEFINE_STRUCT_SUBSET_REF.
73///
74/// @hideinitializer
75// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
76#define USERVER_ALLOW_CONVERSIONS_TO_SUBSET()
77 template <typename Other>
78 requires(USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>()) /*implicit*/ operator Other() const& {
79 return Other::MakeFromSupersetImpl(*this, USERVER_NAMESPACE::utils::impl::InternalTag{});
80 }
81
82 template <typename Other>
83 requires(USERVER_NAMESPACE::utils::impl::IsDefinedAndAggregate<Other>()) /*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_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_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_DEFINE_STRUCT_SUBSET or @ref USERVER_DEFINE_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 }