SST  15.1.0
StructuralSimulationToolkit
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 
23 namespace SST::ELI {
24 
25 template <class Base, class... Args>
26 struct 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 
38 template <class Base, class... CtorArgs>
40 {
41 public:
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 
73 private:
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 
81 template <class Base, class... CtorArgs>
83 {
84 public:
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 
101 template <class Base, class Builder, class... CtorArgs>
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 
119 private:
120  std::string elemlib_;
121  std::string elem_;
122  std::string alias_;
123  Builder* builder_;
124 };
125 
126 template <class Base, class... CtorArgs>
127 bool
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 
135 template <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 
143 template <class Base, class T, class Enable = void>
144 struct Allocator
145 {
146  template <class... Args>
147  T* operator()(Args&&... args)
148  {
149  return new T(std::forward<Args>(args)...);
150  }
151 };
152 
153 template <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 
164 template <class T, class Base, class... Args>
165 struct DerivedBuilder : public Builder<Base, Args...>
166 {
167  Base* create(Args... ctorArgs) override { return Allocator<Base, T>()(std::forward<Args>(ctorArgs)...); }
168 };
169 
170 template <class, class>
171 inline constexpr bool is_tuple_constructible_v = false;
172 
173 template <class T, class... Args>
174 inline 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  {
182  }
183 };
184 
185 template <class Base, class CtorTuple>
187 {};
188 
189 template <class Base, class... Args>
190 struct ElementsBuilder<Base, std::tuple<Args...>>
191 {
192  static BuilderLibrary<Base, Args...>* getLibrary(const std::string& name)
193  {
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 */
210 template <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 
242 template <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 
263 template <class Base, class Ctor, class... Ctors>
264 struct 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 
290 template <int NumValid>
292 {
293  static constexpr bool atLeastOneValidCtor = true;
294 };
295 
296 template <>
298 {};
299 
300 template <class Base>
301 struct 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  {
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:186
Definition: elementbuilder.h:102
Definition: elementbuilder.h:264
Definition: elementbuilder.h:39
Definition: elementbuilder.h:165
Definition: elementbuilder.h:176
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:291
Definition: elibase.h:124
Definition: attributeInfo.h:22
Definition: elementbuilder.h:144
Definition: elementbuilder.h:136
Definition: elibase.h:152
Definition: elementbuilder.h:82
Definition: elementbuilder.h:243
Definition: elementbuilder.h:26
Implements a constructor for a derived base as usually happens with subcomponents, e.g.
Definition: elementbuilder.h:211
Definition: elementbuilder.h:154