SST 15.0
Structural Simulation Toolkit
elementbuilder.h
1// Copyright 2009-2025 NTESS. Under the terms
2// of Contract DE-NA0003525 with NTESS, the U.S.
3// Government retains certain rights in this software.
4//
5// Copyright (c) 2009-2025, NTESS
6// All rights reserved.
7//
8// This file is part of the SST software package. For license
9// information, see the LICENSE file in the top level directory of the
10// distribution.
11
12#ifndef SST_CORE_ELI_ELEMENTBUILDER_H
13#define SST_CORE_ELI_ELEMENTBUILDER_H
14
15#include "sst/core/eli/elibase.h"
16
17#include <map>
18#include <string>
19#include <tuple>
20#include <type_traits>
21#include <utility>
22
23namespace SST::ELI {
24
25template <class Base, class... Args>
26struct Builder
27{
28 using createFxn = Base* (*)(Args...);
29
30 virtual Base* create(Args... ctorArgs) = 0;
31
32 template <class NewBase>
33 using ChangeBase = Builder<NewBase, Args...>;
34
35 virtual ~Builder() = default;
36};
37
38template <class Base, class... CtorArgs>
39class BuilderLibrary
40{
41public:
42 using BaseBuilder = Builder<Base, CtorArgs...>;
43
44 explicit BuilderLibrary(const std::string& name) :
45 name_(name)
46 {}
47
48 BaseBuilder* getBuilder(const std::string& name) { return factories_[name]; }
49
50 const std::map<std::string, BaseBuilder*>& getMap() const { return factories_; }
51
52 void readdBuilder(const std::string& name, const std::string& alias, BaseBuilder* fact)
53 {
54 factories_[name] = fact;
55 if ( !alias.empty() ) factories_[alias] = fact;
56 }
57
58 bool addBuilder(const std::string& elem, const std::string& alias, BaseBuilder* fact)
59 {
60 readdBuilder(elem, alias, fact);
61 return addLoader(name_, elem, alias, fact);
62 }
63
64 bool addBuilder(const std::string& elem, BaseBuilder* fact)
65 {
66 readdBuilder(elem, "", fact);
67 return addLoader(name_, elem, "", fact);
68 }
69
70 template <class NewBase>
71 using ChangeBase = BuilderLibrary<NewBase, CtorArgs...>;
72
73private:
74 bool addLoader(const std::string& elemlib, const std::string& elem, const std::string& alias, BaseBuilder* fact);
75
76 std::map<std::string, BaseBuilder*> factories_;
77
78 std::string name_;
79};
80
81template <class Base, class... CtorArgs>
83{
84public:
85 using Library = BuilderLibrary<Base, CtorArgs...>;
86 using BaseFactory = typename Library::BaseBuilder;
87 using Map = std::map<std::string, Library*>;
88
89 static Library* getLibrary(const std::string& name)
90 {
91 static Map libraries; // Database
92 auto& lib = libraries[name];
93 if ( !lib ) lib = new Library(name);
94 return lib;
95 }
96
97 template <class NewBase>
98 using ChangeBase = BuilderLibraryDatabase<NewBase, CtorArgs...>;
99};
100
101template <class Base, class Builder, class... CtorArgs>
102struct BuilderLoader : public LibraryLoader
103{
104 BuilderLoader(const std::string& elemlib, const std::string& elem, const std::string& alias, Builder* builder) :
105 elemlib_(elemlib),
106 elem_(elem),
107 alias_(alias),
108 builder_(builder)
109 {}
110
111 void load() override
112 {
113 BuilderLibraryDatabase<Base, CtorArgs...>::getLibrary(elemlib_)->readdBuilder(elem_, alias_, builder_);
114 }
115
116 BuilderLoader(const BuilderLoader&) = delete;
117 BuilderLoader& operator=(const BuilderLoader&) = delete;
118
119private:
120 std::string elemlib_;
121 std::string elem_;
122 std::string alias_;
123 Builder* builder_;
124};
125
126template <class Base, class... CtorArgs>
127bool
128BuilderLibrary<Base, CtorArgs...>::addLoader(
129 const std::string& elemlib, const std::string& elem, const std::string& alias, BaseBuilder* fact)
130{
131 auto loader = new BuilderLoader<Base, BaseBuilder, CtorArgs...>(elemlib, elem, alias, fact);
132 return ELI::LoadedLibraries::addLoader(elemlib, elem, alias, loader);
133}
134
135template <class Base, class T>
137{
138 static bool isLoaded() { return loaded; }
139
140 static inline const bool loaded = Base::Ctor::template add<T>();
141};
142
143template <class Base, class T, class Enable = void>
145{
146 template <class... Args>
147 T* operator()(Args&&... args)
148 {
149 return new T(std::forward<Args>(args)...);
150 }
151};
152
153template <class Base, class T>
155{
156 template <class... Args>
157 Base* operator()(Args&&... ctorArgs)
158 {
159 static T* cached = new T(std::forward<Args>(ctorArgs)...);
160 return cached;
161 }
162};
163
164template <class T, class Base, class... Args>
165struct DerivedBuilder : public Builder<Base, Args...>
166{
167 Base* create(Args... ctorArgs) override { return Allocator<Base, T>()(std::forward<Args>(ctorArgs)...); }
168};
169
170template <class, class>
171inline constexpr bool is_tuple_constructible_v = false;
172
173template <class T, class... Args>
174inline constexpr bool is_tuple_constructible_v<T, std::tuple<Args...>> = std::is_constructible_v<T, Args...>;
175
177{
178 template <class T, class... Args>
179 static BuilderLibrary<T, Args...>* getLibrary(const std::string& name)
180 {
181 return BuilderLibraryDatabase<T, Args...>::getLibrary(name);
182 }
183};
184
185template <class Base, class CtorTuple>
187{};
188
189template <class Base, class... Args>
190struct ElementsBuilder<Base, std::tuple<Args...>>
191{
192 static BuilderLibrary<Base, Args...>* getLibrary(const std::string& name)
193 {
194 return BuilderLibraryDatabase<Base, Args...>::getLibrary(name);
195 }
196
197 template <class T>
198 static Builder<Base, Args...>* makeBuilder()
199 {
200 return new DerivedBuilder<T, Base, Args...>();
201 }
202};
203
204/**
205 @class ExtendedCtor
206 Implements a constructor for a derived base as usually happens with subcomponents, e.g.
207 class U extends API extends Subcomponent. You can construct U as either an API*
208 or a Subcomponent* depending on usage.
209*/
210template <class NewCtor, class OldCtor>
212{
213 template <class T>
214 static constexpr bool is_constructible_v = NewCtor::template is_constructible_v<T>;
215
216 /**
217 The derived Ctor can "block" the more abstract Ctor, meaning an object
218 should only be instantiated as the most derived type. enable_if here
219 checks if both the derived API and the parent API are still valid
220 */
221 template <class T>
222 static std::enable_if_t<OldCtor::template is_constructible_v<T>, bool> add()
223 {
224 // if abstract, force an allocation to generate meaningful errors
225 return NewCtor::template add<T>() && OldCtor::template add<T>();
226 }
227
228 template <class T>
229 static std::enable_if_t<!OldCtor::template is_constructible_v<T>, bool> add()
230 {
231 // if abstract, force an allocation to generate meaningful errors
232 return NewCtor::template add<T>();
233 }
234
235 template <class __NewCtor>
236 using ExtendCtor = ExtendedCtor<__NewCtor, ExtendedCtor<NewCtor, OldCtor>>;
237
238 template <class NewBase>
239 using ChangeBase = typename NewCtor::template ChangeBase<NewBase>;
240};
241
242template <class Base, class... Args>
244{
245 template <class T>
246 static constexpr bool is_constructible_v = std::is_constructible_v<T, Args...>;
247
248 template <class T>
249 static bool add()
250 {
251 // if abstract, force an allocation to generate meaningful errors
252 auto* fact = new DerivedBuilder<T, Base, Args...>;
253 return Base::addBuilder(T::ELI_getLibrary(), T::ELI_getName(), SST::ELI::GetAlias<T>::get(), fact);
254 }
255
256 template <class NewBase>
257 using ChangeBase = SingleCtor<NewBase, Args...>;
258
259 template <class NewCtor>
260 using ExtendCtor = ExtendedCtor<NewCtor, SingleCtor<Base, Args...>>;
261};
262
263template <class Base, class Ctor, class... Ctors>
264struct CtorList : public CtorList<Base, Ctors...>
265{
266 template <class T> // if T is constructible with Ctor arguments
267 static constexpr bool is_constructible_v =
268 is_tuple_constructible_v<T, Ctor> // yes, constructible
269 || CtorList<Base, Ctors...>::template is_constructible_v<T>; // not constructible here but maybe later
270
271 template <class T, int NumValid = 0, class U = T>
272 static std::enable_if_t<std::is_abstract_v<U> || is_tuple_constructible_v<U, Ctor>, bool> add()
273 {
274 // if abstract, force an allocation to generate meaningful errors
275 auto* fact = ElementsBuilder<Base, Ctor>::template makeBuilder<U>();
276 Base::addBuilder(T::ELI_getLibrary(), T::ELI_getName(), SST::ELI::GetAlias<T>::get(), fact);
277 return CtorList<Base, Ctors...>::template add<T, NumValid + 1>();
278 }
279
280 template <class T, int NumValid = 0, class U = T>
281 static std::enable_if_t<!std::is_abstract_v<U> && !is_tuple_constructible_v<U, Ctor>, bool> add()
282 {
283 return CtorList<Base, Ctors...>::template add<T, NumValid>();
284 }
285
286 template <class NewBase>
287 using ChangeBase = CtorList<NewBase, Ctor, Ctors...>;
288};
289
290template <int NumValid>
292{
293 static constexpr bool atLeastOneValidCtor = true;
294};
295
296template <>
299
300template <class Base>
301struct CtorList<Base, void>
302{
303 template <class>
304 static constexpr bool is_constructible_v = false;
305
306 template <class T, int numValidCtors>
307 static bool add()
308 {
309 return NoValidConstructorsForDerivedType<numValidCtors>::atLeastOneValidCtor;
310 }
311};
312
313} // namespace SST::ELI
314
315#define ELI_CTOR(...) std::tuple<__VA_ARGS__>
316#define ELI_DEFAULT_CTOR() std::tuple<>
317
318#define SST_ELI_CTORS_COMMON(...) \
319 using Ctor = ::SST::ELI::CtorList<__LocalEliBase, __VA_ARGS__, void>; \
320 template <class __TT, class... __CtorArgs> \
321 using DerivedBuilder = ::SST::ELI::DerivedBuilder<__LocalEliBase, __TT, __CtorArgs...>; \
322 template <class... __InArgs> \
323 static SST::ELI::BuilderLibrary<__LocalEliBase, __InArgs...>* getBuilderLibraryTemplate(const std::string& name) \
324 { \
325 return ::SST::ELI::BuilderDatabase::getLibrary<__LocalEliBase, __InArgs...>(name); \
326 } \
327 template <class __TT> \
328 static bool addDerivedBuilder(const std::string& lib, const std::string& elem) \
329 { \
330 return Ctor::template add<0, __TT>(lib, elem); \
331 }
332
333#define SST_ELI_DECLARE_CTORS(...) \
334 SST_ELI_CTORS_COMMON(ELI_FORWARD_AS_ONE(__VA_ARGS__)) \
335 template <class... Args> \
336 static bool addBuilder(const std::string& elemlib, const std::string& elem, const std::string& alias, \
337 SST::ELI::Builder<__LocalEliBase, Args...>* builder) \
338 { \
339 return getBuilderLibraryTemplate<Args...>(elemlib)->addBuilder(elem, alias, builder); \
340 }
341
342#define SST_ELI_DECLARE_CTORS_EXTERN(...) SST_ELI_CTORS_COMMON(ELI_FORWARD_AS_ONE(__VA_ARGS__))
343
344// VA_ARGS here
345// 0) Base name
346// 1) List of ctor args
347#define SST_ELI_BUILDER_TYPEDEFS(...) \
348 using BaseBuilder = ::SST::ELI::Builder<__VA_ARGS__>; \
349 using BuilderLibrary = ::SST::ELI::BuilderLibrary<__VA_ARGS__>; \
350 using BuilderLibraryDatabase = ::SST::ELI::BuilderLibraryDatabase<__VA_ARGS__>; \
351 template <class __TT> \
352 using DerivedBuilder = ::SST::ELI::DerivedBuilder<__TT, __VA_ARGS__>;
353
354#define SST_ELI_BUILDER_FXNS() \
355 static BuilderLibrary* getBuilderLibrary(const std::string& name) \
356 { \
357 return BuilderLibraryDatabase::getLibrary(name); \
358 } \
359 static bool addBuilder( \
360 const std::string& elemlib, const std::string& elem, const std::string& alias, BaseBuilder* builder) \
361 { \
362 return getBuilderLibrary(elemlib)->addBuilder(elem, alias, builder); \
363 }
364
365// I can make some extra using typedefs because I have only a single ctor
366#define SST_ELI_DECLARE_CTOR(...) \
367 using Ctor = ::SST::ELI::SingleCtor<__LocalEliBase, __VA_ARGS__>; \
368 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase, __VA_ARGS__) \
369 SST_ELI_BUILDER_FXNS()
370
371#define SST_ELI_BUILDER_FXNS_EXTERN() \
372 static BuilderLibrary* getBuilderLibrary(const std::string& name); \
373 static bool addBuilder( \
374 const std::string& elemlib, const std::string& elem, const std::string& alias, BaseBuilder* builder);
375
376#define SST_ELI_DECLARE_CTOR_EXTERN(...) \
377 using Ctor = ::SST::ELI::SingleCtor<__LocalEliBase, __VA_ARGS__>; \
378 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase, __VA_ARGS__); \
379 SST_ELI_BUILDER_FXNS_EXTERN()
380
381#define SST_ELI_DEFINE_CTOR_EXTERN(base) \
382 bool base::addBuilder( \
383 const std::string& elemlib, const std::string& elem, const std::string& alias, BaseBuilder* builder) \
384 { \
385 return getBuilderLibrary(elemlib)->addBuilder(elem, alias, builder); \
386 } \
387 base::BuilderLibrary* base::getBuilderLibrary(const std::string& elemlib) \
388 { \
389 return BuilderLibraryDatabase::getLibrary(elemlib); \
390 }
391
392// I can make some extra using typedefs because I have only a single ctor
393#define SST_ELI_DECLARE_DEFAULT_CTOR() \
394 using Ctor = ::SST::ELI::SingleCtor<__LocalEliBase>; \
395 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase) \
396 SST_ELI_BUILDER_FXNS()
397
398#define SST_ELI_DECLARE_DEFAULT_CTOR_EXTERN() \
399 using Ctor = ::SST::ELI::SingleCtor<__LocalEliBase>; \
400 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase) \
401 SST_ELI_BUILDER_FXNS_EXTERN()
402
403#define SST_ELI_EXTEND_CTOR() using Ctor = ::SST::ELI::ExtendedCtor<LocalCtor, __ParentEliBase::Ctor>;
404
405#define SST_ELI_SAME_BASE_CTOR() \
406 using LocalCtor = __ParentEliBase::Ctor::ChangeBase<__LocalEliBase>; \
407 SST_ELI_EXTEND_CTOR() \
408 using BaseBuilder = typename __ParentEliBase::BaseBuilder::template ChangeBase<__LocalEliBase>; \
409 using BuilderLibrary = __ParentEliBase::BuilderLibrary::ChangeBase<__LocalEliBase>; \
410 using BuilderLibraryDatabase = __ParentEliBase::BuilderLibraryDatabase::ChangeBase<__LocalEliBase>; \
411 SST_ELI_BUILDER_FXNS()
412
413#define SST_ELI_NEW_BASE_CTOR(...) \
414 using LocalCtor = ::SST::ELI::SingleCtor<__LocalEliBase, __VA_ARGS__>; \
415 SST_ELI_EXTEND_CTOR() \
416 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase, __VA_ARGS__) \
417 SST_ELI_BUILDER_FXNS()
418
419#define SST_ELI_DEFAULT_BASE_CTOR() \
420 using LocalCtor = ::SST::ELI::SingleCtor<__LocalEliBase>; \
421 SST_ELI_EXTEND_CTOR() \
422 SST_ELI_BUILDER_TYPEDEFS(__LocalEliBase) \
423 SST_ELI_BUILDER_FXNS()
424
425#endif // SST_CORE_ELI_ELEMENTBUILDER_H
Definition elementbuilder.h:83
Definition elementbuilder.h:40
Definition elementbuilder.h:145
Definition elementbuilder.h:177
Definition elementbuilder.h:103
Definition elementbuilder.h:27
Definition elementbuilder.h:155
Definition elementbuilder.h:265
Definition elementbuilder.h:166
Definition elementbuilder.h:187
Implements a constructor for a derived base as usually happens with subcomponents,...
Definition elementbuilder.h:212
static std::enable_if_t< OldCtor::template is_constructible_v< T >, bool > add()
The derived Ctor can "block" the more abstract Ctor, meaning an object should only be instantiated as...
Definition elementbuilder.h:222
Definition elementbuilder.h:137
Definition elibase.h:124
Definition elementbuilder.h:292
Definition elementbuilder.h:244