Extending classes written in C++ using SWIG

Discussion in 'Python' started by Lars Moastuen, Nov 25, 2004.

  1. 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
     
    Lars Moastuen, Nov 25, 2004
    #1
    1. Advertising

  2. (Lars Moastuen) writes:

    > 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.
     
    Jacek Generowicz, Nov 25, 2004
    #2
    1. Advertising

  3. > (Lars Moastuen) writes:
    >
    >> 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.


    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
     
    Phil Thompson, Nov 25, 2004
    #3
  4. 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.


    "Phil Thompson" <> wrote in message news:<>...
    > > (Lars Moastuen) writes:
    > >
    > >> 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.

    >
    > 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
     
    Lars Moastuen, Nov 26, 2004
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve C. Orr, MCSD
    Replies:
    1
    Views:
    597
    reaway lee
    Aug 24, 2003
  2. stefan
    Replies:
    3
    Views:
    451
    stefan
    Dec 8, 2004
  3. Jean-Baptiste PERIN

    extending python with a C-written dll

    Jean-Baptiste PERIN, Dec 20, 2004, in forum: Python
    Replies:
    24
    Views:
    1,241
    Scott David Daniels
    Dec 21, 2004
  4. KaiWen
    Replies:
    102
    Views:
    2,884
    Jorgen Grahn
    Sep 15, 2011
  5. BENI
    Replies:
    0
    Views:
    109
Loading...

Share This Page