Extending classes written in C++ using SWIG

L

Lars Moastuen

Im trying to extend a class written in C++ in Python and use this
extended class in a C++ call... I made an example to clarify:

-- Foo.h --
#ifndef FOO_H
#define FOO_H

#include <iostream>
using namespace std;

class Foo;
class Bar;

class Bar
{
public:
Bar() {};
~Bar() {};
virtual char* DoBar() const { return "Bar"; };
};

class Foo
{
public:
Foo() {};
~Foo() {};
virtual void DoFoo(Bar* someBar) { cout << someBar->DoBar() << endl;
};
};

#endif

-- Foo.cpp --
#include "Foo.h"

-- swig.i --
%module test
%{
#include "Foo.h"
%}

%include "Foo.h"

-- UseFoo.py --
#!/usr/bin/env python

from test import *;

class ExtendedBar(Bar):
def __init__(self):
Bar.__init__(self);

def DoBar(self):
return "ExtendedBar";

bar = ExtendedBar();
foo = Foo();
foo.DoFoo(bar);

------------

I now expect to get "ExtendedBar" as output from UseFoo.py (since I've
declared DoBar() as virtual, but I get "Bar".... I'm using SWIG 1.3.1
to create the bindings from Python to C++. (swig -python -c++)

Can anyone tell me why? Is there a way to remedy this??

Thx,
Lars Moastuen
 
J

Jacek Generowicz

class Bar
{
public:
Bar() {};
~Bar() {};
virtual char* DoBar() const { return "Bar"; };
};

class Foo
{
public:
Foo() {};
~Foo() {};
virtual void DoFoo(Bar* someBar) { cout << someBar->DoBar() << endl;
};
};
class ExtendedBar(Bar):
def __init__(self):
Bar.__init__(self);

def DoBar(self):
return "ExtendedBar";

bar = ExtendedBar();
foo = Foo();
foo.DoFoo(bar);
I now expect to get "ExtendedBar" as output from UseFoo.py (since I've
declared DoBar() as virtual, but I get "Bar"
Can anyone tell me why?

Because "someBar->DoBar()" uses the vtable of the C++ dynamic type to
decide which actual DoBar method to call. Unfortunately there is no
C++ type corresponding to your ExtendedBar, there is no vtable for
your ExtendedBar type in C++. C++ only knows about C++ types; it is
unaware of the existence of Python types. Inside your Python instance
of ExtendedBar you are holding on to an instance of a C++ Bar.
Is there a way to remedy this??

I don't know whether SWIG provides a boxed solution for this sort of
problem. One approach is to override the DoBar method in a subclass of
Bar in C++, and make that method pass the 'dispatch request' up into
Python. Then you expose the wrapper class in Python, rather than Bar
itself.

It looks something like this:

struct PseudoBar : public Bar {
PyObject* self; // the Python instance wrapping this C++ instance
void DoFoo () {
PyObject_CallMethod(this->self, "DoFoo", "");
}
};

Of course, you'll need to augment this with checks to ensure that
there really is something overriding the method, otherwise you'll end
up in an infinite loop ... but hopefully you get the idea. It's all a
bit tedious.
 
P

Phil Thompson

Because "someBar->DoBar()" uses the vtable of the C++ dynamic type to
decide which actual DoBar method to call. Unfortunately there is no
C++ type corresponding to your ExtendedBar, there is no vtable for
your ExtendedBar type in C++. C++ only knows about C++ types; it is
unaware of the existence of Python types. Inside your Python instance
of ExtendedBar you are holding on to an instance of a C++ Bar.


I don't know whether SWIG provides a boxed solution for this sort of
problem. One approach is to override the DoBar method in a subclass of
Bar in C++, and make that method pass the 'dispatch request' up into
Python. Then you expose the wrapper class in Python, rather than Bar
itself.

It looks something like this:

struct PseudoBar : public Bar {
PyObject* self; // the Python instance wrapping this C++ instance
void DoFoo () {
PyObject_CallMethod(this->self, "DoFoo", "");
}
};

Of course, you'll need to augment this with checks to ensure that
there really is something overriding the method, otherwise you'll end
up in an infinite loop ... but hopefully you get the idea. It's all a
bit tedious.

If SWIG really requires you to do this by hand (I'm surprised) then you
might want to look at SIP (http://www.riverbankcomputing.co.uk/sip/). SIP
generates code that does exactly what you suggest so that bindings behave
as the OP was expecting.

Phil
 
L

Lars Moastuen

Thx for your replies.

It seems I have created an example I thought were equal to my problem,
but it turned out that wasn't the case. I'm trying to figure out what
the difference is, but I find the classes quite similar to the example
below (too big to post here)... However, this problem can be solved by
using "directors" (http://www.swig.org/Doc1.3/Python.html#directors),
mine cannot...

Im currently testing if Boost.Python does the job better, but seems I
run into the same problem... I suspect it has something to do with STL
list or something, but I'm not sure yet...

Will post if I manage to create an example or if I find a solution.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top