tags:

views:

540

answers:

2

I'm trying to add a python callback to a C++ library as illustrated:

template<typename T> void doCallback(shared_ptr<T> data) {
   PyObject* pyfunc; //I have this already
   PyObject* args = Py_BuildValue("(O)", data);
   PyEval_CallObject(pyfunc,args);
}

This fails because data hasn't gone through swig, and isn't a PyObject.

I tried using:

swigData = SWIG_NewPointerObj((void*)data, NULL, 0);

But because its a template, I don't really know what to use for the second parameter. Even if I do hard code the 'correct' SWIGTYPE, it usually segfaults on PyEval_CallObject.

So my questions are:

  1. Whats the best way to invoke swig type wrapping?

  2. Am I even going in the right direction here? Directors looked promising for implementing a callback, but I couldn't find an example of directors with python.

Update: The proper wrapping is getting generated. I have other functions that return shared_ptrs and can call those correctly.

A: 

shared_ptr<T> for unknown T isn't a type, so SWIG can't hope to wrap it. What you need to do is provide a SWIG wrapping for each instance of shared_ptr that you intend to use. So if for example you want to be able to doCallback() with both shared_ptr<Foo> and shared_ptr<Bar>, you will need:

  • A wrapper for Foo
  • A wrapper for Bar
  • Wrappers for shared_ptr<Foo> and shared_ptr<Bar>.

You make those like so:

namespace boost {
    template<class T> class shared_ptr
      {
      public:
          T * operator-> () const;
      };
  }

%template(FooSharedPtr) boost::shared_ptr<Foo>;
%template(BarSharedPtr) boost::shared_ptr<Bar>;
David Seiler
Thanks for the response David! Unfortunately I already have the code you describe, so a wrapping is getting generated.
rogueg
A: 

My first answer misunderstood the question completely, so let's try this again.

Your central problem is the free type parameter T in the definition of doCallback. As you point out in your question, there's no way to make a SWIG object out of a shared_ptr<T> without a concrete value for T: shared_ptr<T> isn't really a type.

Thus I think that you have to specialize: for each concrete instantiation of doCallback that the host system uses, provide a template specialization for the target type. With that done, you can generate a Python-friendly wrapping of data, and pass it to your python function. The simplest technique for that is probably:

swigData = SWIG_NewPointerObj((void*)(data.get()), SWIGType_Whatever, 0);

...though this can only work if your Python function doesn't save its argument anywhere, as the shared_ptr itself is not copied.

If you do need to retain a reference to data, you'll need to use whatever mechanism SWIG usually uses to wrap shared_ptr. If there's no special-case smart-pointer magic going on, it's probably something like:

pythonData = new shared_ptr<Whatever>(data);
swigData = SWIG_NewPointerObj(pythonData, SWIGType_shared_ptr_to_Whatever, 1);

Regardless, you then you have a Python-friendly SWIG object that's amenable to Py_BuildValue().

Hope this helps.

David Seiler
Makes sense. I was hoping there was some magic swig way to insert the SWIGType_really_long_name in my templated code, but I guess not.
rogueg