capydi
Loading...
Searching...
No Matches
CreationalConfigDispatcher.hpp
Go to the documentation of this file.
1#ifndef CREATIONAL_CONFIG_DISPATCHER_HPP_
2#define CREATIONAL_CONFIG_DISPATCHER_HPP_
3
9#include "capydi/Error.hpp"
10
17#include <expected>
18#include <utility>
19#include <boost/mp11.hpp>
20#include <variant>
21
22
23namespace capy::di
24{
25
26template<typename T>
28
29template<CreationalConfig... Configs>
31{
32public:
33 constexpr explicit CreationalConfigDispatcher(
34 Configs&&... configs
35 )
36 : configs_tuple_ { std::move(configs)... }
37 , configs_dispatch_map_ {
38 populate_configs_map(this->configs_tuple_)
39 }
40 {}
41
42public:
43
44 template<
45 typename Type,
46 typename KeyPack = meta::Pack<Type>,
47 typename InputType = std::tuple<>
48 >
51 ) const {
52 auto maybe_config = this->configs_dispatch_map_.static_find(meta::Unit<KeyPack>{});
53
54 if constexpr (!decltype(maybe_config)::has_value())
55 {
56 return std::expected<meta::RuntimeRef<Type>, Error> {
57 std::unexpected { Error::CANNOT_BE_RESOLVED }
58 };
59 }
60 else
61 {
63 auto configs_array_reference = maybe_config.value();
64 typename decltype(configs_array_reference)::ReferenceType configs_array = configs_array_reference;
65 auto input_tuple = context.input;
66
67 ResolutionOverrides overrides = std::apply(
68 []<typename... T>(T&&... input_args) {
69 return ResolutionOverrides {
70 std::forward<T>(input_args)...
71 };
72 },
73 std::move(input_tuple)
74 );
75
76 for (auto& config_variant : configs_array)
77 {
78 auto resolution = std::visit([this, &overrides, &context](auto& config_reference) mutable {
79 typename std::decay_t<decltype(config_reference)>::ReferenceType config = config_reference;
80 using DependenciesPack = dependencies_pack_t<std::remove_reference_t<decltype(config)>>;
81
82 auto maybe_dependencies_tuple = this->resolve_dependencies_tuple(
83 context,
84 config,
85 DependenciesPack{}
86 );
87
88 return maybe_dependencies_tuple
89 .and_then([&config, &overrides, &context](auto&& dependencies_tuple) mutable {
90 return config.do_resolve(KeyPack{}, dependencies_tuple, context, overrides);
91 });
92 }, config_variant);
93
94
95 if (!resolution.has_value()) [[unlikely]]
96 {
97 code = resolution.error();
98 continue;
99 }
100
101 if (!overrides.validate()) [[unlikely]]
102 {
104 continue;
105 }
106
107 overrides.reset();
108 return resolution;
109 }
110
111 return std::expected<meta::RuntimeRef<Type>, Error> {
112 std::unexpected { code }
113 };
114 }
115 }
116
117private:
118
119 template<typename... Dependencies>
121 resolve_dependencies_tuple(
123 const auto& config,
125 ) const
126 {
127 auto dependencies_tuple = [this, &config, &context]<std::size_t... Idx>(std::index_sequence<Idx...>) {
128 return std::tuple {
129 [this, &config, &context] {
130 auto dependencies_input = config.template get_dependencies_input<Idx>();
131
132 if (dependencies_input.has_value()) {
133 return context.container.template resolve<Dependencies>(dependencies_input.value());
134 } else {
135 return context.container.template resolve<Dependencies>();
136 }
137 }()...
138 };
139 }(std::index_sequence_for<Dependencies...>{});
140
141 return std::apply(
142 [](auto&&... maybe_dependencies) {
143 using DependenciesTuple = std::tuple<
144 typename std::remove_reference_t<decltype(maybe_dependencies)>::value_type...
145 >;
146
147 if ((maybe_dependencies.has_value() && ...))
148 {
149 return std::expected<DependenciesTuple, Error> {
150 std::tuple { std::move(maybe_dependencies.value())... }
151 };
152 }
153 else
154 {
155 return std::expected<DependenciesTuple, Error> {
156 std::unexpected { Error::DEPENDENCY_CANNOT_BE_RESOLVED }
157 };
158 }
159 },
160 std::move(dependencies_tuple)
161 );
162 }
163
164 template<typename UniqueType, typename... NonUniqueConfigs>
165 static constexpr auto collect(NonUniqueConfigs&... args)
166 {
167 using namespace boost::mp11;
168
169 auto configs_tuple = std::tuple_cat(
170 ([&] {
171 if constexpr (meta::pack_contains_t<
173 UniqueType
174 >) {
175 return std::tuple<meta::RuntimeRef<NonUniqueConfigs>> {
176 meta::RuntimeRef<NonUniqueConfigs> { args }
177 };
178 }
179 else {
180 return std::tuple<>{};
181 }
182 }())...
183 );
184
185 return std::apply(
186 []<typename... T>(T&&... configs) {
187 using UniqueTs = mp_unique<mp_list<T...>>;
189 return std::array<VariantType, sizeof...(configs)> {
190 VariantType { std::forward<T>(configs) }...
191 };
192 },
193 std::move(configs_tuple)
194 );
195 }
196
197 template<meta::wrapped_with<std::tuple> ConfigsTuple>
198 static constexpr auto populate_configs_map(
199 ConfigsTuple& configs_tuple
200 ) {
201 using namespace boost::mp11;
202
203 using KeysList = mp_flatten<mp_list<
206 meta::Pack,
207 mp_list
208 >...
209 >>;
210
211 using UniqueKeysList = mp_unique<KeysList>;
212
213 return [&]<typename... UniqueKeys>(mp_list<UniqueKeys...>&& list) {
214 return meta::MetaMap {
215 meta::KVPair {
216 meta::Unit<UniqueKeys>{},
217 std::apply(
218 [](auto&... args) {
219 return collect<UniqueKeys>(args...);
220 },
221 configs_tuple
222 )
223 }...
224 };
225 }(UniqueKeysList{});
226 }
227
228private:
229 using ConfigsTupleType = std::tuple<Configs...>;
230 using MapType = decltype(populate_configs_map(std::declval<ConfigsTupleType&>()));
231
232 ConfigsTupleType configs_tuple_;
233 MapType configs_dispatch_map_;
234};
235
236}
237
238#endif // !CREATIONAL_CONFIG_DISPATCHER_HPP_
Concept and utilities for creational (factory/constructor) configurations.
Error codes and diagnostics for dependency injection operations.
Compile-time type pack utilities and metaprogramming foundations.
Resolution concept and result type for dependency injection queries.
constexpr Resolution< Type, Error > auto resolve(meta::wrapped_with< ResolutionContext > auto &context) const
Definition CreationalConfigDispatcher.hpp:49
constexpr CreationalConfigDispatcher(Configs &&... configs)
Definition CreationalConfigDispatcher.hpp:33
Definition ResolutionOverrides.hpp:15
constexpr bool validate() const
Definition ResolutionOverrides.hpp:58
constexpr void reset() const
Definition ResolutionOverrides.hpp:65
Definition CreationalConfigDispatcher.hpp:27
Concept for configurations that handle dependency creation and instantiation.
Definition CreationalConfig.hpp:50
Definition Resolution.hpp:30
Definition WrappedWIth.hpp:30
Definition Decorator.hpp:19
typename Config::DependenciesPack dependencies_pack_t
Definition CreationalConfig.hpp:82
typename Config::ResolutionKeysPack resolution_keys_pack_t
Helper alias to extract the resolution keys from a CreationalConfig.
Definition CreationalConfig.hpp:79
Error
Enumeration of possible errors during dependency injection resolution.
Definition Error.hpp:26
@ CANNOT_BE_RESOLVED
A dependency cannot be resolved because no suitable config was found.
Definition Error.hpp:28
@ DEPENDENCY_CANNOT_BE_RESOLVED
A dependency required by another dependency cannot be resolved. This indicates a missing transitive d...
Definition Error.hpp:32
@ NOT_ALL_INPUTS_RETRIEVED
Definition Error.hpp:43
typename typed__::Rebind< Type, SrcContainer, DstContainer >::type rebind_t
Definition Rebind.hpp:121
constexpr bool create_static_method_exists_and_is_unique_v
Definition FunctionTraits.hpp:46
constexpr bool pack_contains_t
Definition Contains.hpp:13
A compile-time heterogeneous type list.
Definition Pack.hpp:70
A zero-cost wrapper for forwarding compile-time type information.
Definition Pack.hpp:45