[C++-sig] Interface design.

David Abrahams dave at boost-consulting.com
Wed Apr 23 00:09:06 CEST 2003


Bruno (Nicodemus) and Ralf,

it would be super if we could get this stuff into the official docs
somewhere.  Perhaps in the tutorial or in a "Techniques" section?
Thoughts?

"Ralf W. Grosse-Kunstleve" <rwgk at yahoo.com> writes:

> Hi!
>
> This is a very nice overview of component based development with Boost.Python!
> I'd like to add one more great discovery (thanks to Dave):
>
> It is easily possible to add member functions/methods from Python.
> For example:
>
> In C++:
>
>     class_<point>("point") ... ;
>
> Now in Python, e.g. in Bruno's __init__.py file:
>
> # a regular function
> def point_repr(self):
>   return str((self.x, self.y))
>
> # now we turn it into a member function
> point.__repr__ = point_repr
>
> *All* point instances created from C++ will have this pure-Python
> member function!
>
> We use this trick to:
>
>   - keep all I/O related code out of our C++ libraries (no trouble with
>     redirecting)
>   - cut down compile times to zero for these additional functions
>   - reduce the memory footprint to virtually zero
>   - minimize the need to recompile
>   - rapid prototyping (you can move the code to C++ if required without
>     changing the interface)
>   - have less source code

Exercise for the reader: finish the following proof-of-concept code
which uses metaclasses to allow a nicer syntax for injection of
additional methods:

    # The one Boost.Python uses for all wrapped classes.
    class BoostPythonMetaclass(type): pass

    # some Boost.Python wrapped class
    class foo(object):
        __metaclass__ = BoostPythonMetaclass

    class injector(object):
        class __metaclass__(BoostPythonMetaclass):
            def __init__(self, name, bases, dict):
                for b in bases:
                    if type(b) not in (self, type):
                        for k,v in dict.items():
                            setattr(b,k,v)
                return type.__init__(self, name, bases, dict)

    # inject some methods in foo
    class more_foo(injector, foo):
        def __repr__(self):
            return 'foobar'
        def ga(self, x):
            return getattr(self, x)

    print repr(foo())
    print foo().ga('__repr__')

Its biggest problem is that it sets some attributes it shouldn't,
like __module__ and, well, __metaclass__ ;-)

> Another useful idea is to replace constructors with factory functions:
>
> _point = point
>
> def point(x=0, y=0):
>   return _point(x, y)
>
> In this simple case there is not much gained, but for constructurs with
> many overloads and/or arguments this is often a great simplification, again
> with virtually zero memory footprint and zero compile-time overhead for
> the keyword support.

Yep, another nice technique!

> --- Nicodemus <nicodemus at globalite.com.br> wrote:
>> Hi!
>> 
>> Suppose you want to have a top level package in python named kikura, 
>> with two sub-modules, core and node, so that a user uses your package 
>> like this:
>> 
>>     import kikura.core
>>     import kikura.node
>>     kikura.core.Foo()
>> 
>> 
>> You can acomplish this generating the two sub-modules (core and node) 
>> using Boost.Python, like any other module:
>> 
>>     // file core.cpp
>>     BOOST_PYTHON_MODULE(core)
>>     {
>>        ...
>>     }
>> 
>>     // file node.cpp
>>     BOOST_PYTHON_MODULE(node)
>>     {
>>        ...
>>     }
>> 
>>     And creating the following directory structure:
>> 
>>     /kikura
>>         __init__.py 
>>         core.pyd
>>         node.pyd
>> 
>> 
>> And that's it (the file __init__.py indicates that the directory is a 
>> package. It can be empty, but it can be useful to provide a more 
>> friendly interface for your users. See below).
>> 
>> I suggest that you name your bindings _core and _node, thought, and make 
>> the sub-modules *python* modules which import the funcionality of the 
>> bindings, like so:
>> 
>>     /kikura
>>         __init__.py
>>         /core
>>             __init__.py
>>             _core.pyd
>>         /node
>>             __init__.py
>>             _node.pyd
>> 
>> 
>> with /kikura/core/__init__.py contains the statement "from _core import 
>> *". The same for the node package. Why do it like this? Well, because 
>> then you can easily implement some things in python in a way that is 
>> transparent for the user (I am assuming here that you're developing this 
>> library). Suppose you want to add a new utility function into the core 
>> package, made in python. You just create a file "foo.py" and put it 
>> inside /kikura/core, and add this line to its __init__.py:
>> 
>>     from foo import util_function, other_function
>> 
>> 
>> and your users will access it like so:
>> 
>>     import kikura.core
>>     kikura.core.util_function(10)
>> 
>> 
>> Back to the compilation time issue, notice that you don't have to write 
>> all class_ declarations inside the BOOST_PYTHON_MODULE macro directly, 
>> you can split them in any number of files you like. For example:
>> 
>>     // file Foo.cpp
>>     #include <kikura/Foo.h>
>>     void export_foo()
>>     {
>>         class_<Foo>()...
>>     }
>> 
>>     // file Bar.cpp
>>     #include <kikura/Bar.h>
>>     void export_bar()
>>     {
>>         class_<Bar>()...
>>     }
>> 
>>     // file core.cpp
>>     void export_foo();
>>     void export_bar();
>>     BOOST_PYTHON_MODULE(core)
>>     {
>>         export_foo();
>>         export_bar();
>>     }
>> 
>> 
>> This will take longer to compile than a single file (because the 
>> Boost.Python headers must be compiled for each cpp), but if your 
>> developing the library this is a big help, because a change in a header 
>> doesn't mean the entire bindings will have to be recompiled.
>> 
>> Hope that helps,
>> Nicodemus.
>
>
> __________________________________________________
> Do you Yahoo!?
> The New Yahoo! Search - Faster. Easier. Bingo
> http://search.yahoo.com
>
> _______________________________________________
> C++-sig mailing list
> C++-sig at python.org
> http://mail.python.org/mailman/listinfo/c++-sig

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com





More information about the Cplusplus-sig mailing list