Repository navigation
Expand file tree
/
Copy pathctdi.cppm
More file actions
191 lines (163 loc) · 7.73 KB
/
Copy pathctdi.cppm
File metadata and controls
191 lines (163 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (C) 2026 mxreal64
// Licensed under the GPL-3.0 license
//
export module ctdi;
import <meta>;
import <type_traits>;
import <tuple>;
import <utility>;
import <optional>;
import <vector>;
import <array>;
import <cstddef>;
namespace ctdi {
template <typename... Ts>
struct TypeList {
static constexpr std::size_t size = sizeof...(Ts);
};
// Internal metadata collectors to build a TypeList safely
template <typename List, typename T> struct AppendToTypeList;
template <typename... Ts, typename T>
struct AppendToTypeList<TypeList<Ts...>, T> {
using type = TypeList<Ts..., T>;
};
// ------------------------------------------------------------------
// Dependency extraction
//
// FIX: the original implementation tried to accumulate a TypeList by
// reassigning `using FinalList = ...` *inside* a `template for` body.
// Each iteration of `template for` is its own scope, so that
// reassignment never escaped the loop — the function always returned
// TypeList<>. There is no mutable "using" state to fold over like that.
//
// Fix: collect the qualifying member reflections into a runtime
// std::vector<std::meta::info> during the consteval call (legal in
// C++26 consteval), freeze it into a static array with
// std::define_static_array, and then convert that array into a type
// pack via splicing under an index_sequence expansion. This is the
// standard "array of std::meta::info -> type pack" pattern for P2996.
// ------------------------------------------------------------------
template <typename T>
consteval auto CollectFieldInfos() {
using CleanType = std::decay_t<T>;
std::vector<std::meta::info> fields;
for (std::meta::info member :
std::meta::members_of(^^CleanType, std::meta::access_context::unchecked())) {
if (std::meta::is_variable(member)) {
fields.push_back(member);
}
}
return fields;
}
template <typename T>
consteval auto ExtractDependencies() {
using CleanType = std::decay_t<T>;
// Anchor the filtered member list at a stable compile-time address.
static constexpr auto fields = std::define_static_array(CollectFieldInfos<CleanType>());
return [] <std::size_t... I> (std::index_sequence<I...>) {
// The Raw Pointer Audit: halt if any field is an unmanaged raw pointer.
// Run once per field before building the list, so the assertion
// message points at the actual offending registration.
([] {
using FieldType = typename [: std::meta::type_of(fields[I]) :];
static_assert(!std::is_pointer_v<FieldType>,
" HARD DISMISSAL: Secure architecture violation! "
"Raw pointers are forbidden in registered services.");
}(), ...);
return TypeList<typename [: std::meta::type_of(fields[I]) :]...>{};
}(std::make_index_sequence<fields.size()>{});
}
template <typename T>
using GetDependencies_t = decltype(ExtractDependencies<T>());
export enum class Lifetime { Transient, Singleton };
export template <typename T, Lifetime L>
struct ServiceDescriptor {
using ServiceType = T;
static constexpr Lifetime lifetime = L;
};
// Metaprogramming graph resolution helper traits
template <typename T, typename List> struct Contains;
template <typename T, typename... Ts>
struct Contains<T, TypeList<Ts...>>
: std::bool_constant<(std::is_same_v<std::decay_t<T>, std::decay_t<Ts>> || ...)> {};
template <typename T, typename List> constexpr bool Contains_v = Contains<T, List>::value;
template <typename T, typename List> struct Append;
template <typename T, typename... Ts> struct Append<T, TypeList<Ts...>> { using type = TypeList<Ts..., T>; };
// Deep recursive validation pass for tracking circular dependency loops.
// This now actually walks real dependency lists, since ExtractDependencies
// is fixed above — previously it only ever recursed into TypeList<>.
template <typename Target, typename ContainerList, typename PathList>
constexpr bool ValidateDependencyGraph() {
using CleanTarget = std::decay_t<Target>;
if constexpr (Contains_v<CleanTarget, PathList>) {
static_assert(!Contains_v<CleanTarget, PathList>,
" COMPILE-TIME ERROR: Circular Dependency Loop Detected!");
return false;
}
else if constexpr (!Contains_v<CleanTarget, ContainerList>) {
static_assert(Contains_v<CleanTarget, ContainerList>,
" COMPILE-TIME ERROR: Required Dependency missing from registration!");
return false;
}
else {
using Deps = GetDependencies_t<CleanTarget>;
return []<typename... Ds>(TypeList<Ds...>) {
using NewPath = typename Append<CleanTarget, PathList>::type;
return (ValidateDependencyGraph<Ds, ContainerList, NewPath>() && ...);
}(Deps{});
}
}
export template <typename... Registrations>
class CompileTimeDI {
private:
using RegisteredTypes = TypeList<typename Registrations::ServiceType...>;
// FIX: singletons are now constructed lazily, on first resolve(),
// rather than eagerly as a plain default-constructed member. This:
// 1. actually threads resolved dependencies into the constructor
// (previously they were default-constructed with no args and
// would fail to compile for any type without a default ctor);
// 2. sidesteps singleton-depends-on-singleton construction-order
// problems, since each singleton is only built the first time
// it's actually asked for.
template <typename T> struct Wrapper {
mutable std::optional<T> instance;
};
using SingletonStorageTuple = std::tuple<Wrapper<typename Registrations::ServiceType>...>;
mutable SingletonStorageTuple mutable_storage;
static constexpr bool ValidateAll() {
return (ValidateDependencyGraph<typename Registrations::ServiceType, RegisteredTypes, TypeList<>>() && ...);
}
static_assert(ValidateAll(), "DI Tree validation failed.");
template <typename T>
static constexpr Lifetime GetLifetime() {
Lifetime found = Lifetime::Transient;
((std::is_same_v<std::decay_t<T>, std::decay_t<typename Registrations::ServiceType>>
? (found = Registrations::lifetime)
: found), ...);
return found;
}
template <typename T>
constexpr std::decay_t<T> Construct() const {
using Deps = GetDependencies_t<std::decay_t<T>>;
return []<typename... Ds>(TypeList<Ds...>, const auto& self) -> std::decay_t<T> {
return std::decay_t<T>{ self.template resolve<std::decay_t<Ds>>()... };
}(Deps{}, *this);
}
public:
constexpr CompileTimeDI() noexcept = default;
template <typename T>
[[nodiscard]] constexpr decltype(auto) resolve() const {
static_assert(Contains_v<T, RegisteredTypes>, " Requested root type is not registered.");
constexpr Lifetime L = GetLifetime<T>();
if constexpr (L == Lifetime::Singleton) {
auto& wrapper = std::get<Wrapper<std::decay_t<T>>>(mutable_storage);
if (!wrapper.instance.has_value()) {
wrapper.instance = Construct<T>();
}
return (*wrapper.instance);
} else {
return Construct<T>();
}
}
};
} // namespace ctdi