I am wrapping a C++ class X using Boost.Python. At the moment an object of this class is created, I would like to insert an additional reference to this object into the local namespace (so that I can refer to his newly created object by a fixed name, let's say lastX). I tried
to do this in the X::X() constructor using
X::X()
{
boost::python::object locals(boost::python::borrowed(PyEval_GetLocals()));
boost::python::object me(this);
locals["lastX"]=me;
}
but this does not work (lastX gets created but it refers to nothing; printing it from Python leads to a segfault). I should probably use my own init function but I do not know how to get a reference to the newly created Python object there either.
Any ideas? Thanks.
To accomplish this, one must modify another frame on the call stack. Be warned, this is dependent on the Python implementation. For example, in Python 2.7, the
inspectmodule andsys.settrace()can be used to modifylocals()on a specific frame.I would highly recommend using a Python solution, as was done in this answer, and monkey patch the desired class'
__init__function. For instance, the following would patch theSpamclass to insert a variable namedlast_spamthat references the newly constructedSpaminstance into the caller's frame:Nevertheless, the same can be accomplished with Boost.Python. To accomplish this, one must:
locals().selfinstance during object construction.Be warned, these can be fairly advance topics.
Modify a frame's
locals()This is the same approach used in this answer, which is dependent on the Python implementation, but written in C++. In most execution paths, a frame's
locals()cannot have new variables written into it. However, when system tracing is enabled, frame tracing functions, often used by debuggers and other tools, can modify a frame'slocals().With the above code, one can use
inject_into_frame_locals()to update a given frame's locals. For example, the following will add a variablexthat references42into the current frame:Access the
selfinstance during object construction.Given a C++ object, one cannot use the Boost.Python API to locate the Python object in which the object is held. Thus, one must access
selfas Boost.Python is constructing the object.There are a few customization points:
Modify the class being exposed to accept
PyObject*during construction. One can have Boost.Python provide thePyObject*instance be specializinghas_back_reference:Expose the
Tas being held by aHeldTypethat derives fromT. When theHeldTypeis publicly derived from theT, it will be providedPyObject*during construction:Suppress Boost.Python from generating a default initializer with
boost::python::no_init, then register a custom factory function as the__init__method with custom policies. One key function for customizing object construction is theboost::python::make_constructorfunction. When provided a pointer to a C++ function or pointer-to-member-function, it will return a Python callable object, which when called, will invoke the function and create the Python object. However, Boost.Python attempts to hide Python-specific details from the C++ functions, and thusselfis not explicitly provided. Nevertheless, one can use a a custom CallPolicy, and access the Python arguments within either theprecallorpostcallfunctions. In order to access theselfargument, one has to be aware of an implementation detail withmake_constructor's policies, which offsets argument access by 1. For instance, when attempting to access theselfargument that resides at index0, one must request the-1index.Once again, the default initializer can be suppressed and the object returned by
make_constructorcan be decorated. When the__init__method is invoked, the decorator object will be invoked where it will be provided all the arguments for the initializer, includingself. The decorator will need to delegate to the object returned frommake_constructor. Useboost::python::make_function()to transform a C++ functor to a callable Python object. Please note that themake_functiondocumentation does not state it supports custom functors. However, it is used internally for functor support.Here is a complete example demonstrating all of the above approaches:
Interactive usage: