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