|
1 | 1 | #pragma once |
2 | 2 |
|
3 | | -#include <type_traits> |
4 | 3 | #include "util/evaluate_type.h" |
| 4 | +#include <memory> |
| 5 | +#include <type_traits> |
| 6 | +#include <utility> |
5 | 7 |
|
6 | 8 | namespace IOC { |
7 | 9 |
|
8 | | - template <typename TService> |
| 10 | + // Factory keyed by both the service type AND the requesting descriptor. |
| 11 | + // The descriptor parameter defaults to `void` so the common case ("one |
| 12 | + // factory per service type") keeps the simpler descriptor-less form. |
| 13 | + // Specialise with an explicit descriptor when several descriptors share |
| 14 | + // the same service type but need different construction recipes (e.g. |
| 15 | + // multiple `SpScBytesQueue` instances with different names/capacities). |
| 16 | + // |
| 17 | + // Lookup precedence (see ResolveFactory_t below): |
| 18 | + // 1. ServiceFactory<TService, TDescriptor> (user-provided per-descriptor) |
| 19 | + // 2. ServiceFactory<TService, void> (auto / default) |
| 20 | + template <typename TService, typename TDescriptor = void> |
9 | 21 | struct ServiceFactory; |
10 | 22 |
|
| 23 | + // Default specialisation: descriptor-less, default-constructs the service. |
11 | 24 | template <typename TService> |
12 | | - struct ServiceFactory { |
| 25 | + struct ServiceFactory<TService, void> { |
13 | 26 | static constexpr TService Create(auto&) { |
14 | | - static_assert(std::is_default_constructible_v<TService>); |
| 27 | + static_assert(std::is_default_constructible_v<TService>, |
| 28 | + "Service has no descriptor parameters and is not default-constructible. " |
| 29 | + "Specialise IOC::ServiceFactory<TService> (or " |
| 30 | + "IOC::ServiceFactory<TService, TDescriptor>) with a custom Create()."); |
15 | 31 | return TService(); |
16 | 32 | } |
| 33 | + |
| 34 | + static auto CreateUnique(auto& container) { |
| 35 | + if constexpr (std::is_default_constructible_v<TService>) { |
| 36 | + return std::make_unique<TService>(); |
| 37 | + } else { |
| 38 | + return std::unique_ptr<TService>(new TService(Create(container))); |
| 39 | + } |
| 40 | + } |
17 | 41 | }; |
18 | 42 |
|
19 | | - template <template<typename ...> class TService, typename... TDescriptors> |
20 | | - struct ServiceFactory<TService<TDescriptors...>> { |
| 43 | + // Templated-service specialisation: resolves each TDescriptor from the |
| 44 | + // container and forwards it to the actual constructor. |
| 45 | + template <template <typename...> class TService, typename... TDescriptors> |
| 46 | + struct ServiceFactory<TService<TDescriptors...>, void> { |
| 47 | + using TActualServiceType = |
| 48 | + typename Util::ReplaceDescriptors<TService<TDescriptors...>>::TResult; |
| 49 | + |
21 | 50 | static constexpr auto Create(auto& container) { |
22 | | - using TActualServiceType = typename Util::ReplaceDescriptors<TService<TDescriptors...>>::TResult; |
| 51 | + static_assert( |
| 52 | + std::is_constructible_v<TActualServiceType, |
| 53 | + decltype(container.template Resolve<TDescriptors>())...>, |
| 54 | + "Service is not constructible from its resolved dependencies. " |
| 55 | + "Either fix the constructor signature or specialise IOC::ServiceFactory<...>."); |
| 56 | + return TActualServiceType(container.template Resolve<TDescriptors>()...); |
| 57 | + } |
23 | 58 |
|
24 | | - if constexpr (std::is_constructible_v<TActualServiceType, decltype(container.template Resolve<TDescriptors>())...>) { |
25 | | - return TActualServiceType(container.template Resolve<TDescriptors>()...); |
26 | | - } else { |
27 | | - return TActualServiceType(); |
28 | | - } |
| 59 | + static auto CreateUnique(auto& container) { |
| 60 | + static_assert( |
| 61 | + std::is_constructible_v<TActualServiceType, |
| 62 | + decltype(container.template Resolve<TDescriptors>())...>, |
| 63 | + "Service is not constructible from its resolved dependencies (CreateUnique)."); |
| 64 | + return std::make_unique<TActualServiceType>( |
| 65 | + container.template Resolve<TDescriptors>()...); |
29 | 66 | } |
30 | 67 | }; |
31 | 68 |
|
| 69 | + // Selects the matching ServiceFactory specialisation for a (Service, |
| 70 | + // Descriptor) pair. If the user specialised |
| 71 | + // `ServiceFactory<TService, TDescriptor>` it is picked; otherwise the |
| 72 | + // descriptor-less `ServiceFactory<TService, void>` is used. |
| 73 | + // |
| 74 | + // Detection: the primary `ServiceFactory<TService, TDescriptor>` is only |
| 75 | + // forward-declared, so `sizeof(ServiceFactory<T, D>)` is valid *only* when |
| 76 | + // (a) D == void (the default partial spec is complete), or (b) the user |
| 77 | + // supplied a descriptor-keyed specialisation. Probe via SFINAE and use a |
| 78 | + // descriptor-keyed factory only when D != void. |
| 79 | + namespace detail { |
| 80 | + |
| 81 | + template <typename TService, typename TDescriptor, typename = void> |
| 82 | + struct HasDescriptorKeyedFactory : std::false_type {}; |
| 83 | + |
| 84 | + template <typename TService, typename TDescriptor> |
| 85 | + struct HasDescriptorKeyedFactory< |
| 86 | + TService, |
| 87 | + TDescriptor, |
| 88 | + std::void_t<decltype(sizeof(ServiceFactory<TService, TDescriptor>))>> |
| 89 | + : std::bool_constant<!std::is_void_v<TDescriptor>> {}; |
| 90 | + |
| 91 | + } // namespace detail |
| 92 | + |
| 93 | + template <typename TService, typename TDescriptor> |
| 94 | + using ResolveFactory_t = |
| 95 | + std::conditional_t<detail::HasDescriptorKeyedFactory<TService, TDescriptor>::value, |
| 96 | + ServiceFactory<TService, TDescriptor>, |
| 97 | + ServiceFactory<TService, void>>; |
| 98 | + |
32 | 99 | } |
0 commit comments