As a very basic example of wrapping a class, let’s consider the
Boost.Python object defines a Python class in C++. It is templated, and in this case we
have the following template arguments:
SessionReaderis the class that will be wrapped
std::shared_ptr<SessionReader>indicates that this object should be stored inside a shared (or smart) pointer, which we frequently use throughout the library, as can be seen by the frequent use of
Boost.Pythonshouldn’t try to automatically wrap the copy constructor of
SessionReader. We add this here because of compiler errors due to subclasses used inside
SessionReader, but generally, this should be used for abstract classes which can’t be copied.
We then have two arguments:
"SessionReader"is the name of the class in Python.
py::no_initindicates this object has no publically-accessible initialiser. This is because for
SessionReader, we define a factory-type function called
We then call the
.def function on the
class_<>, which allows us to define member functions
on our class. This is equivalent to
def-ing a function in Python.
.def has two required
parameters, and one optional parameter:
Boost.Python is very smart and can convert many Python objects to their equivalent C++
function arguments, and C++ return types of the function to their respective Python object.
Many times therefore, one only needs to define the
However, there are some instances where we need to do some additional conversion, mask some C++ complexity from the Python interface, or deal with functions that return references. We describe ways to deal with this below.
Instead of defining a function pointer to a member of the C++ class, we can define a function pointer to a separate function that defines some extra functionality. This is called a thin wrapper.
As an example, consider the
CreateInstance function. In C++ we pass this function the
command line arguments in the usual
argv format. In Python, command line
arguments are defined as a list of strings inside
does not know how to convert this list to
argc, argv, so we need some additional
In Python, we can then simply call
session = SessionReader.CreateInstance(sys.argv).
When dealing with functions in C++ that return references, e.g.
&GetFactor() we need to supply an additional argument to
.def(), since Python immutable
types such as strings and integers cannot be passed by reference. For a full list of
options, consult the
Boost.Python guide. However a good rule of thumb is to use
copy_const_reference as highlighted above, which will create a copy of the const reference
and return this.
The LibUtilities/Python/BasicUtils/SharedArray.cpp file contains a number of
functions that allow for the automatic conversion of Nektar++
Array<OneD, > storage to and
ndarray objects. This means that you can wrap functions that take these
as parameters and return arrays very easily. However bear in mind the following
ndarraycreated from an
Array<OneD, >(and vice versa) will share their memory. Although this avoids expensive memory copies, it means that changing the C++ array changes the contents of the NumPy array (and vice versa).
Use thin wrappers to overcome this problem. For examples of how to do this,
particularly in returning tuples, consult the
which contains numerous examples.
ThreeDarrays are not supported.
More information on the memory management and how the memory is shared can be found in Section 23.
Nektar++ makes heavy use of inheritance, which can be translated to Python quite easily
Boost.Python. For a good example of how to do this, you can examine the
StdExpansion and its elements such as
StdQuadExp. In a cut-down form, these
look like the following:
Note the following:
StdExpansionis an abstract class, so it has no initialiser and is non-copyable.
py::bases<StdExpansion>in the definition of
StdQuadExpto define its parent class. This does not necessarily need to include the full hierarchy of C++ inheritance: in
StdRegionsthe inheritance graph for
StdExpansion -> StdExpansion2D -> StdQuadExp. In the above wrapper, we omit the StdExpansion2D call entirely.
py::init<>is used to show how to wrap a C++ constructor. This can accept any arguments for which you have either written explicit wrappers or
Boost.Pythonalready knows how to convert.
Most Nektar++ enumerators come in the form:
To wrap this, you can use the
NEKPY_WRAP_ENUM macro defined in
NekPyConfig.hpp, which in
this case can be used as
NEKPY_WRAP_ENUM(MyEnum, MyEnumMap). Note that if instead
const char * the map is defined as a
const std::string, you can use the