Nektar++
Python/BasicUtils/NekFactory.hpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: NekFactory.hpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Python convenience wrappers for NekFactory.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#ifndef NEKTAR_LIBUTILITIES_PYTHON_BASICUTILS_NEKFACTORY_HPP
36#define NEKTAR_LIBUTILITIES_PYTHON_BASICUTILS_NEKFACTORY_HPP
37
40
41#include <functional>
42#include <type_traits>
43#include <utility>
44
45//
46// The following code is adapted from:
47// https://stackoverflow.com/questions/21192659/variadic-templates-and-stdbind
48//
49// The objective is to define an alternative to std::placeholders that can be
50// used in combination with variadic templates to wrap arbitrary functions with
51// arguments defined through a parameter pack.
52//
53
54template <std::size_t> struct placeholder_template
55{
56};
57
58namespace std
59{
60template <std::size_t N>
61struct is_placeholder<placeholder_template<N>>
62 : integral_constant<std::size_t, N + 1>
63{
64};
65} // namespace std
66
67/**
68 * @brief Helper class to be used in NekFactory_Register. Stores the Python
69 * function used to call the creator function.
70 */
71#pragma GCC visibility push(hidden)
72template <class T> class NekFactoryRegisterHelper
73{
74public:
75 NekFactoryRegisterHelper(py::object obj) : m_obj(obj)
76 {
77 }
78
80
81 /**
82 * @brief Callback to Python instantiator function (typically a class
83 * constructor). Assumes that all arguments can be converted to boost.python
84 * through converter infrastructure.
85 */
86 template <class... Args> std::shared_ptr<T> create(Args... args)
87 {
88 // Create an object using the Python callable.
89 py::object inst = m_obj(args...);
90
91 // Assume that it returns an object of the appropriate type.
92 return py::cast<std::shared_ptr<T>>(inst);
93 }
94
95protected:
96 /// Python function that is used to construct objects.
97 py::object m_obj;
98};
99#pragma GCC visibility pop
100
101template <typename tFac> class NekFactory_Register;
102
103/**
104 * @brief Lightweight wrapper for the factory RegisterCreatorFunction, to
105 * support the ability for Python subclasses of @tparam tBase to register
106 * themselves with the NekFactory.
107 *
108 * This function wraps NekFactory::RegisterCreatorFunction. This function
109 * expects a function pointer to a C++ object that will construct an instance of
110 * @tparam tBase. In this case we therefore need to construct a function call
111 * that will construct our Python object (which is a subclass of @tparam tBase),
112 * and then pass this back to Boost.Python to give the Python object back.
113 *
114 * We have to do some indirection here to get this to work, but we can
115 * achieve this with the following strategy:
116 *
117 * - Create a @c NekFactoryCapsuleDestructor object, which as an argument will
118 * store the Python class instance that will be instantiated from the Python
119 * side.
120 * - Using std::bind, construct a function pointer to the helper's creation
121 * function, NekFactoryCapsuleDestructor<tBase>::create.
122 * - Create a Python capsule that will contain the @c
123 * NekFactoryCapsuleDestructor<tBase> instance, and register this in the
124 * global namespace of the current module. This then ties the capsule to the
125 * lifetime of the module.
126 */
127template <template <typename, typename, typename...> typename tFac,
128 typename tBase, typename tKey, typename... tParam>
129class NekFactory_Register<tFac<tKey, tBase, tParam...>>
130{
131public:
132 NekFactory_Register(tFac<tKey, tBase, tParam...> &fac) : m_fac(fac)
133 {
134 }
135
136 void operator()(tKey &name, py::object &obj, std::string const &nameKey)
137 {
138 // Create a factory register helper, which will call the C++ function to
139 // create the factory.
142
143 // Register this with the factory using std::bind to grab a function
144 // pointer to that particular object's function. We use
145 // placeholder_template to bind to some number of arguments.
146 DoRegister(name, helper, std::index_sequence_for<tParam...>{});
147
148 // Create a capsule that will be embedded in the __main__ namespace. So
149 // deallocation will occur, but only once Python ends or the Python
150 // module is deallocated.
151 std::string key = "_" + nameKey;
152
153 // Allocate a capsule to ensure memory is cleared up upon deallocation
154 // (depends on Python 2 or 3).
155 py::capsule capsule(helper, [](void *ptr) {
158 delete tmp;
159 });
160
161 // Embed the capsule in __main__.
162 py::globals()[key.c_str()] = capsule;
163 }
164
165private:
166 /**
167 * @brief Helper function to unpack parameter arguments into the factory's
168 * register creation function using the #placeholder_template helper struct.
169 */
170 template <std::size_t... Is>
172 std::integer_sequence<std::size_t, Is...>)
173 {
174 m_fac.RegisterCreatorFunction(
175 name,
176 std::bind(
178 helper, placeholder_template<Is>{}...));
179 }
180
181 /// Reference to the NekFactory.
182 tFac<tKey, tBase, tParam...> &m_fac;
183};
184
185#endif
void operator()(tKey &name, py::object &obj, std::string const &nameKey)
void DoRegister(tKey &name, NekFactoryRegisterHelper< tBase > *helper, std::integer_sequence< std::size_t, Is... >)
Helper function to unpack parameter arguments into the factory's register creation function using the...
tFac< tKey, tBase, tParam... > & m_fac
Reference to the NekFactory.
Helper class to be used in NekFactory_Register. Stores the Python function used to call the creator f...
~NekFactoryRegisterHelper()=default
std::shared_ptr< T > create(Args... args)
Callback to Python instantiator function (typically a class constructor). Assumes that all arguments ...
py::object m_obj
Python function that is used to construct objects.
STL namespace.