SWIG and the Abstract Base Class

View: New views
7 Messages — Rating Filter:   Alert me  

SWIG and the Abstract Base Class

by Bob Hood :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

The lengthy intro:

I have been employing SWIG in a fairly lightweight context within my application, wrapping an internal function that takes a component reference and then executes it.  This provides a highly simplified interface within Python, were I can just call the function with the component name and some arguments.

However, my application now has a functional SDK, and it's time for the rubber to meet the road.  The philosophy within the system (and the SDK) is "code to an interface".  This means that plug-ins for my application must inherit from one or more C++ abstract base classes.  In order to directly afford Python programmers the ability to write plug-ins for my application, I need to provide this same functionality to them.

SWIG won't completely wrap C++ abstract base classes (rightly so), leaving the wrappers with __init__() functions that throw an AttributeError.  So, I need an approach that allows the Python environment to utilize these interfaces such that they are duck-typed as a valid plug-in for my application.

The (imagined) approach:

The SWIG documentation talks about wrapping C++ abstract classes to the extent that it (basically) can't be usefully done.  It does not, from what I've been able to find, talk about how it can be done.  So, the approach I'm going to try (and for which I would appreciate some sage feedback) is to first create "shell" proxy classes in C++ that inherit from my SDK's interfaces.  In a file called, for example, "AbstractWrappers.h",
where the inherited classes are those with pure virtual methods, they would look  something like this:

    [...]
    class ProxyPlugin : public SDK::Plugin
    {
    public:
        ProxyPlugin() : Plugin()
        {}
        virtual ~ProxyPlugin()
        {}

        virtual void* get_interface(unsigned int interface_id)
        {
            return 0;
        }
    };
    [...]
    class ProxyCommand : public SDK::Command
    {
    public:
        ProxyCommand() : Command()
        {}
        virtual ~ProxyCommand()
        {}

        virtual void execute(Context& ctx)
        {}
        virtual void execute_without_item()
        {}
        virtual void execute(const ItemRef& item)
        {}
    };
    [...]
    // Convenience proxy

    class ProxyCommandPlugin : public ProxyPlugin, public ProxyCommand
    {
        [...]
    }

And so forth.  The proxy classes would implement do-nothing versions of the pure virtual methods, leaving the Python versions to override.

Then, in the SWIG interface file, I would wrap these proxy classes:

    [...]
    %include <SDK/Plugin.h>
    %include <SDK/interfaces/CommandInterface.h>

    %include "AbstractWrappers.h"
    [...]

With the proxy classes wrapped, I would then turn on the "directors" feature for each of them:

    %module (naturalvar="1",directors="1") PySDK
    [...]
    %include <SDK/Plugin.h>
    %include <SDK/interfaces/CommandInterface.h>

    %feature("director") ProxyPlugin;
    %feature("director") ProxyCommand;
    %feature("director") ProxyCommandPlugin;

    %include "AbstractWrappers.h"

Then, in the Python environment, I would inherit from the proxy classes in order to create my Pythonic application plug-in classes:

    class CommandPlugin(object, ProxyCommandPlugin)
        def __init__(self):
            super(CommandPlugin, self).__init__()
    [...]

Which would then allow me to interact with the Python CommandPlugin class instance in C++ as though it were either a SDK::Plugin or an SDK::Command.

The question:

Is this the best way to approach wrapping C++ abstract base classes, or am I way off base?  Does SWIG offer something more elegant than this?  I'd like an approach that makes plug-in writing within the scripting environment as close as possible to that of the C++ (binary) case, so if there are improvements to be made on this approach, I would greatly appreciate knowing about it before I move forward.

All my thanks.


Render me gone,                       |||
Bob                                 ^(===)^
---------------------------------oOO--(_)--OOo---------------------------------
           The world needs more cults.  They attract stupidity like a
                magnet attracts metal, and they're self cleaning.

------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

Re: SWIG and the Abstract Base Class

by nitro-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Am 25.09.2009, 04:56 Uhr, schrieb Bob Hood <bhood2@...>:
>
> The question:
>
> Is this the best way to approach wrapping C++ abstract base classes, or
> am I way off base?  Does SWIG offer something more elegant than this?
> I'd like an approach that makes plug-in writing within the scripting
> environment as close as possible to that of the C++ (binary) case, so if
> there are improvements to be made on this approach, I would greatly
> appreciate knowing about it before I move forward.

Sorry for the half-finished last mail. When I hit enter to go into the  
next line my mail client went all crazy, opened windows and sent the  
message.

 From your explanation I couldn't understand why you are creating those  
shell proxy classes.

Iirc there are a few places in my own code where I just inherit from a  
swig wrapped abstract base class. I've enabled directors on this abstract  
base class before of course. Then I can just use it like I'd do in C++.  
E.g.

On the C++ side

class InheritMe
{
public:
     virtual int DoSomething() = 0;
};

Then on the python side:

import myModule

class Derived(myModule.InheritMe):
     def DoSomething(self):
         return 666

should just work.

-Matthias

------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

Re: SWIG and the Abstract Base Class

by Bob Hood :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Nitro wrote:
From your explanation I couldn't understand why you are creating those shell proxy classes.

[...]

should just work.

For some reason, for me, it didn't.  Any attempts to use the ABCs in this fashion threw the __init__()'s AttributeError.

I'll have a look again and see if I'm doing something wrong.

Thanks for the feedback, Matthias.



Render me gone,                       |||
Bob                                 ^(===)^
---------------------------------oOO--(_)--OOo---------------------------------
           The world needs more cults.  They attract stupidity like a
                magnet attracts metal, and they're self cleaning.


------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

Re: SWIG and the Abstract Base Class

by Bob Hood :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Nitro wrote:
> From your explanation I couldn't understand why you are creating those
> shell proxy classes.

Ok, the problem I'm running into is the need to initialize the base
class.  In the original post, you can see that I'm needing to propagate
the initialization to the abstract base class:

    [...]
    class ProxyPlugin : public SDK::Plugin
    {
    public:
        ProxyPlugin() : Plugin()
        [...]

This means that I cannot simply perform this action from a direct
inheritance within Python without triggering SWIG's AttributeError throw
in the wrapper.  In other words, this direct inheritance:

    class CommandPlugin(object, Plugin)
        def __init__(self):
            super(CommandPlugin, self).__init__()

would cause the AttributeError to be thrown.

In your example, your arrangement doesn't appear to have a need to call
any methods in the abstract base class, so you probably would not be
encountering this.

This is why I went with the proxy classes.  Direct inheritance of an
abstract base class that requires initialization gives me exception
heartburn using SWIG.  This is the main issue I'm trying to address.


Render me gone,                       |||
Bob                                 ^(===)^
---------------------------------oOO--(_)--OOo---------------------------------
           The world needs more cults.  They attract stupidity like a
                magnet attracts metal, and they're self cleaning.



------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

Re: SWIG and the Abstract Base Class

by nitro-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Am 27.09.2009, 19:29 Uhr, schrieb Bob Hood <bhood2@...>:

>     class CommandPlugin(object, Plugin)
>         def __init__(self):
>             super(CommandPlugin, self).__init__()
>
> would cause the AttributeError to be thrown.

Attached is a very simplistic interface file. Command line to run: "swig  
-python -c++ test.i". I used swig 1.3.39.

I didn't compile the resulting wrapper code, just looked at the generated  
python code. It doesn't contain the AttributeError you mention. The  
AttributeError in __init__ is often generated if you do not apply  
directors to the base class properly.

The second problem is your python code does not work for me. It should  
look like

class CommandPlugin(Plugin):
     def __init__(self):
         super(CommandPlugin, self).__init__()

Note the difference in the base classes. Plugin already inherits from  
object, so no need to inherit from it again. Of course if you absolutely  
want to inherit from object again, you can do

class CommandPlugin(Plugin, object):
     def __init__(self):
         super(CommandPlugin, self).__init__()

Note the difference in the order of the base classes.

HTH,
-Matthias

Appendix A: generated python __init__ code:

     def __init__(self, *args):
         if self.__class__ == BaseClass:
             _self = None
         else:
             _self = self
         this = _test.new_BaseClass(_self, *args)
         try: self.this.append(this)
         except: self.this = this

------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

test.i (286 bytes) Download Attachment

Re: SWIG and the Abstract Base Class

by Bob Hood :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Apologies for the delays in replying.  I am ill.  :(

Nitro wrote:
> I didn't compile the resulting wrapper code, just looked at the
> generated python code. It doesn't contain the AttributeError you
> mention. The AttributeError in __init__ is often generated if you do
> not apply directors to the base class properly.

Which is precisely what I was doing wrong, it would seem.  I don't know
how I missed that.  I must have done something incorrectly from the
outset, and it snowballed from there.  Vielen Dank, Matthias, for your
patience and your help.  A second set of eyes on a problem is always a
tremendous benefit.


Render me gone,                       |||
Bob                                 ^(===)^
---------------------------------oOO--(_)--OOo---------------------------------
           The world needs more cults.  They attract stupidity like a
                magnet attracts metal, and they're self cleaning.



------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user

Re: SWIG and the Abstract Base Class

by nitro-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Am 29.09.2009, 17:33 Uhr, schrieb Bob Hood <bhood2@...>:

> Apologies for the delays in replying.  I am ill.  :(
>
> Nitro wrote:
>> I didn't compile the resulting wrapper code, just looked at the
>> generated python code. It doesn't contain the AttributeError you
>> mention. The AttributeError in __init__ is often generated if you do
>> not apply directors to the base class properly.
>
> Which is precisely what I was doing wrong, it would seem.  I don't know
> how I missed that.  I must have done something incorrectly from the
> outset, and it snowballed from there.  Vielen Dank, Matthias, for your
> patience and your help.  A second set of eyes on a problem is always a
> tremendous benefit.

I'm glad I could help. A useful thing especially for debugging swig  
related problems is to write a very simple interface file containing the  
problem. Very often this will lead to an immediate insight into what's  
going wrong and why.

-Matthias

------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Swig-user mailing list
Swig-user@...
https://lists.sourceforge.net/lists/listinfo/swig-user