Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Base/Factory.hpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.
7 : : All rights reserved. See the LICENSE file for details.
8 : : \brief Factory utilities
9 : : \details Factory utilities. The functions defined in this file help
10 : : interfacing with object factories. For a short introduction on what
11 : : factories are good for, see
12 : : http://www.boost.org/doc/libs/release/libs/functional/factory.
13 : : */
14 : : // *****************************************************************************
15 : : #ifndef Factory_h
16 : : #define Factory_h
17 : :
18 : : #include <functional>
19 : :
20 : : #include "NoWarning/factory.hpp"
21 : : #include "NoWarning/value_factory.hpp"
22 : :
23 : : #include "Exception.hpp"
24 : :
25 : : namespace tk {
26 : :
27 : : //! Register class into factory with given key. This is used to register a
28 : : //! derived-class object's constructor (deriving from some base class) to a
29 : : //! factory. The factory itself is a std::map< key, std::function< Child*() > >,
30 : : //! i.e., an associative container, associating some key to a std::function
31 : : //! object holding a pointer of Child's base class constructor. The constructor
32 : : //! and its bound arguments are stored via boost::factory, which, in this
33 : : //! use-case, yields the correct function object of type Base constructor pointer
34 : : //! and thus facilitates runtime polymorphism. This function works in conjunction
35 : : //! with boost::factory, i.e., uses reference semantics (works with storing
36 : : //! pointers of objects). For a simple example on how to use this function, see
37 : : //! tests/unit/Base/Factory.h.
38 : : //! \param[in] f Factory to register to (std::map with value using reference
39 : : //! semantics)
40 : : //! \param[in] key Key used to identify the entry in the factory
41 : : //! \param[in] args Variable number of arguments to pass to the constructor
42 : : //! being registered. Note that the constructor arguments are only bound to
43 : : //! the constructor and stored in the factory (an std::map with value using
44 : : //! reference semantics). The object is not instantiated here, i.e., the
45 : : //! constructor is not called here. The object can be instantiated by function
46 : : //! instantiate. \see instantiate
47 : : template< class C, class Key, class Factory, typename... ConstructorArgs >
48 : : void record( Factory& f, const Key& key, ConstructorArgs&&... args ) {
49 [ + - ][ + - ]: 3 : f.emplace( key,
[ + - ][ + - ]
50 : : std::bind( boost::factory< C* >(),
51 : : std::forward< ConstructorArgs >( args )... ) );
52 : : }
53 : :
54 : : //! Instantiate object from factory. Factory must have a mapped_value which must
55 : : //! have a result_type ptr, e.g., std::map< Key, std::function< Obj*() > >. This
56 : : //! wrapper function can be used to instantiate an derived-class object from a
57 : : //! factory, repeatedly filled with wrapper function 'record' above. The
58 : : //! factory, as described in the documentation of 'record', stores base class
59 : : //! pointers in an associative container, thereby facilitating runtime
60 : : //! polymorphism and a simple lookup-and-instantiate-style object creation. The
61 : : //! object instantiated is of type Child class. This function works in
62 : : //! conjunction with boost::factory, i.e., uses reference semantics (works with
63 : : //! storing pointers of objects). For a simple example on how to
64 : : //! use this function, see tests/unit//Base/Factory.h.
65 : : //! \param[in] f Factory to instantiate object from (std::map with value using
66 : : //! reference semantics)
67 : : //! \param[in] key Key used to identify the object to instantiate from factory
68 : : //! \return std::unique_ptr pointing to the object instantiated from factory
69 : : //! \see record
70 : : template< class Factory, class Key,
71 : : class Obj = typename std::remove_pointer<
72 : : typename Factory::mapped_type::result_type >::type >
73 : 2 : std::unique_ptr< Obj > instantiate( const Factory& f, const Key& key ) {
74 : : const auto it = f.find( key );
75 : : Assert( it != end( f ), "No such object registered in factory" );
76 : 2 : return std::unique_ptr< Obj >( it->second() );
77 : : }
78 : :
79 : : //! Register "model" class of "host" into factory with given key. This wrapper
80 : : //! can be used to in a similar manner to 'record', but uses
81 : : //! boost::value_factory to bind the model object constructor to its arguments
82 : : //! and place it in the associative container storing host class objects. The
83 : : //! container is thus of type std::map< key, std::function< T() > >, i.e.,
84 : : //! associating a key to a function holding a constructor (and not its
85 : : //! pointer). Runtime polymorphism here is realized entirely within the "base"
86 : : //! class. See walker::DiffEq in DiffEq/DiffEq.h for an example and more
87 : : //! information on runtime polymorphism without client-side inheritance. As a
88 : : //! result, this wrapper works with factories that use value semantics, as opposed
89 : : //! to 'record' and instantiate which work with reference semantics factories.
90 : : //! In order to differentiate between runtime polymorphic classes using
91 : : //! reference semantics, consistent with classes realizing runtime polymorphism
92 : : //! without client-side inheritance, we call Host as the "Base" class and Model
93 : : //! as the "derived" (or child) class. This wrapper function works in
94 : : //! conjunction with boost::value_factory, i.e., uses value semantics (works
95 : : //! with storing objects instead of object pointers). For a simple example on
96 : : //! how to use this function, see tests/unit//Base/Factory.h.
97 : : //! \param[in] f Factory to register to (std::map with value using value
98 : : //! semantics)
99 : : //! \param[in] key Key used to identify the entry in the factory
100 : : //! \param[in] args Variable number of arguments to pass to the constructor
101 : : //! being registered. Note that the constructor arguments are only bound to
102 : : //! the constructor and stored in the factory (an std::map with value using
103 : : //! value semantics). The object is not instantiated here, i.e., the
104 : : //! constructor is not called here. The object can be instantiated by simply
105 : : //! calling the function call operator () on the mapped value. For an example,
106 : : //! RNGStack::selected() in RNG/RNGStack.C.
107 : : template< class Host, class ModelConstructor, class Factory, class Key,
108 : : typename... ModelConstrArgs >
109 : 2 : void recordModel( Factory& f, const Key& key, ModelConstrArgs&&... args ) {
110 : : // Bind model constructor to its arguments
111 [ + - ]: 2 : std::function< ModelConstructor() > c =
112 : : std::bind( boost::value_factory< ModelConstructor >(),
113 : : std::forward< ModelConstrArgs >( args )... );
114 : : // Bind host to std::function of model constructor and place in factory
115 [ - + ][ - - ]: 2 : f.emplace( key, std::bind( boost::value_factory< Host >(), std::move(c) ) );
116 : 2 : }
117 : :
118 : : //! Register model class of host into factory with given key using late binding.
119 : : //! This variant of 'record' is very similar to 'recordModel', but registers a
120 : : //! model class constructor to a factory with late binding of the constructor
121 : : //! argument. Late binding allows specifying the constructor argument at the
122 : : //! time when the object is instantiated instead of at the time when it is
123 : : //! registered. This has all the benefits of using a factory and allows passing
124 : : //! information into the model object only when it is available. The late bind
125 : : //! is facilitated via std::bind instead of std::bind using a placeholder,
126 : : //! _1, which stands for the first argument (bound later, i.e., not here). The
127 : : //! value of the model constructor argument is then not used here, only its
128 : : //! type, used to perform the late binding. The binding happens to both the
129 : : //! model constructor via std::function (passed to the host constructor) as well
130 : : //! as explicitly to the host constructor. Prescribing late binding to the model
131 : : //! constructor ensures that the compiler requires the argument to the model
132 : : //! constructor, i.e., ensures that the host constructor is required to pass the
133 : : //! argument to the model constructor. Prescribing late binding to the host
134 : : //! constructor puts in the actual request that an argument (with the correct
135 : : //! type) must be passed to the host constructor at instantiate time, which then
136 : : //! will forward it to the model constructor. See also, for example,
137 : : //! walker::DiffEq's corresponding constructor. An example of client-side code
138 : : //! is in walker::DiffEqStack::registerDiffEq for registration into factory, and
139 : : //! DiffEqStack::createDiffEq for instantiation late-passing the argument.
140 : : //! \param[in] f Factory to register to (std::map with value using value
141 : : //! semantics)
142 : : //! \param[in] key Key used to identify the entry in the factory
143 : : //! \warning Only works with a single constructor argument
144 : : template< class Host, class ModelConstructor, class Factory, class Key >
145 : 177138 : void recordModelLate( Factory& f, const Key& key ) {
146 : : // Prescribe late binding the model constructor to its single argument
147 [ + - ]: 177138 : std::function< ModelConstructor() > c =
148 : : std::bind( boost::value_factory< ModelConstructor >() );
149 : : // Bind host to std::function of model constructor and place in factory and
150 : : // also explicitly bind single model constructor argument to host constructor
151 [ - + ][ - - ]: 177138 : f.emplace( key, std::bind( boost::value_factory< Host >(), std::move(c) ) );
152 : 177138 : }
153 : :
154 : : //! Register Charm++ model class of host into factory with given key. We bind a
155 : : //! host constructor to its arguments of which the first one is a std::function
156 : : //! holding a model constructor type (modeling, i.e., used polymorhically with
157 : : //! host), followed by an optional number of others (possibly zero) with
158 : : //! arbitrary types. Note that the model constructor is a nullptr (default-
159 : : //! constructed) and only used to forward its type to the call site inside
160 : : //! std::function. The host constructor function is then placed into the
161 : : //! factory. This is because Charm++ chares do not explicitly invoke
162 : : //! constructors, only call ckNew() on their proxy, which requires all
163 : : //! constructor arguments to be present and forwarded to the actual constructor
164 : : //! that is only called at a later point in time. This can then be used by those
165 : : //! constructors of hosts that invoke the model constructors' proxies' ckNew()
166 : : //! and ignore the std::function. See, e.g., rngtest::Battery() and the
167 : : //! associated unit tests in tests/unit//Base/Factory.h.
168 : : //! \param[in] f Factory to register to (std::map with value using value
169 : : //! semantics)
170 : : //! \param[in] key Key used to identify the entry in the factory
171 : : //! \param[in] args Variable number of arguments to pass to the constructor
172 : : //! being registered.
173 : : template< class Host, class ModelConstructor, class Factory, class Key,
174 : : typename... ModelConstrArgs >
175 [ + - ]: 2 : void recordCharmModel( Factory& f, const Key& key, ModelConstrArgs&&... args ) {
176 [ + + ][ - + ]: 3 : f.emplace( key, std::bind( boost::value_factory< Host >(),
[ - - ]
177 : : std::function< ModelConstructor() >(), // nullptr
178 : : std::forward< ModelConstrArgs >( args )...) );
179 : 2 : }
180 : :
181 : : } // tk::
182 : :
183 : : #endif // Factory_h
|