Reputation: 3665
I Have a question regarding swig wrapped objects generated on the Python side and wrapped objects generated on the C++ side. Suppose I have the following simple C++ class definitions
#include <vector>
class Sphere
{
public:
Sphere(){};
};
class Container
{
public:
Container() : data_(0) {};
void add() {
data_.push_back(Sphere());
}
Sphere & get(int i) { return data_[i]; }
std::vector<Sphere> data_;
};
and the following swig setup
%module engine
%{
#define SWIG_FILE_WITH_INIT
#include "sphere.h"
%}
// -------------------------------------------------------------------------
// Header files that should be parsed by SWIG
// -------------------------------------------------------------------------
%feature("pythonprepend") Sphere::Sphere() %{
print 'Hello'
%}
%include "sphere.h"
If I then do the following in Python
import engine
sphere_0 = engine.Sphere()
container = engine.Container()
container.add()
sphere_1 = container.get(0)
Then the first instantiation of the wrapped Sphere class does call the init method of the Python wrapping interface ('Hello' is printed).
However, the second, where the instance is generated on the C++ side does not ('Hello' is not printed).
Since my goal is to be able to add additional Python functionality to the object upon its construction, I'd be pleased to hear if anybody has any pointers for a correct approach to achieve this - for both of the above instantiation scenarios.
Best regards,
Mads
Upvotes: 1
Views: 645
Reputation: 8109
I usually do things like this with explicit pythoncode
blocks in the interface file:
%pythoncode %{
def _special_python_member_function(self):
print "hello"
self.rotate() # some function of Sphere
Sphere.new_functionality=_special_python_member_function
%}
So you can add arbitrary python functionality to the class, on top of what the SWIG interface provides. You may want/need to rename
some of the C functionality out the way but this can should get you all of the member functions you want.
I've never tried to remap __init__
in this way, so I don't know how that would behave. Assuming that it won't work, you won't be able to ensure that the python objects have a given internal state (member variables) at construction.
What you will be forced to do is do lazy evaluation:
def function_that_depends_on_python_specific_state(self, *args):
if not hasatttr( self, 'python_data'):
self.python_data = self.make_python_data() # construct the relevant data
pass # do work that involves the python specific data
and check for the existence of the python specific data. If there is just a few cases
of this, I'd just put it in the functions as above. However, if that ends up being
messy, you could modify __getattr__
so that it constructs the python-specific data
members as they are accessed.
def _sphere_getattr(self, name):
if name=='python_data':
self.__dict__[name]=self.make_python_data()
return self.__dict__[name]
else:
raise AttributeError
Sphere.__getattr__ = _sphere_getattr
IMHO, in the limit where you have a large amount of new functionality, and data that are independent of the underlying C implementation, you are in effect asking "How can I make my python Sphere class be a sub-class ofthe C Sphere class but keep them as the same type?"
Upvotes: 1