Multi-module development

Introduction

It is a common practices to construct final program or a package from few different dependent or independent C++ libraries. Many time these libraries reuse classes\functions defined in some other library. I think this is a must requirement from a code generator to be able to expose these libraries to Python , without “re-exposing” the class\functions definition twice.

This functionality is new in version “0.8.6”.

Use case introduction

Lets say that you have to expose few libraries, which deal with image processing:

  • core library - defines base class for all image classes - image_i
  • png library - defines class png_image_t, which derives from core::image_i and implements functionality for “png” image format.

The code:

namespace core{
    class image_i{
        ...
        virtual void load() = 0;
    };
} //core

namespace png{
    class png_image_t : public core::image_i{
        ...
        virtual void load();
    };
}

The desired goal is to expose every class in its own package.

already_exposed

Every Py++ declaration has already_exposed property. This property says to Py++ that the declaration is already exposed in another module:

#generate_code.py script

mb_core = module_builder_t( ... )
mb_core.class_( 'image_i' ).include()
mb_core.build_code_creator( 'core' )
mb.write_module( 'core.cpp' )

mb_png = module_builder_t( ... )
mb_png.class_( '::core::image_i' ).already_exposed = True
mb_png.class_( '::png::png_image_t' ).include()
mb_core.build_code_creator( 'png' )
mb.write_module( 'png.cpp' )

Py++ will generate code very similar to the the following one:

//file core.cpp
namespace bp = boost::python;

struct image_i_wrapper : core::image_i, bp::wrapper< core::image_i > {
    image_i_wrapper()
    : core::image_i(), bp::wrapper< core::image_i >()
    {}

    virtual void load(  ){
        bp::override func_load = this->get_override( "load" );
        func_load(  );
    }
    ...
};

BOOST_PYTHON_MODULE(core){
    bp::class_< image_i_wrapper, boost::noncopyable >( "image_i" )
        ...
        .def( "load", bp::pure_virtual( &::core::image_i::load ) );
}
//file png.cpp
struct png_image_t_wrapper : png::png_image_t, bp::wrapper< png::png_image_t > {

    png_image_t_wrapper()
    : png::png_image_t(), bp::wrapper< png::png_image_t >()
    {}

    virtual void load(  ) {
        if( bp::override func_load = this->get_override( "load" ) )
            func_load(  );
        else
            this->png::png_image_t::load(  );
    }

    void default_load(  ) {
        png::png_image_t::load( );
    }
};

BOOST_PYTHON_MODULE(pyplusplus){
    bp::class_< png_image_t_wrapper, bp::bases< core::image_i > >( "png_image_t" )
    //-------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^
      ...
      .def( "load", &::png::png_image_t::load, &png_image_t_wrapper::default_load );
}

As you can see “png.cpp” file doesn’t contains code, which exposes core::image_i class.

Semi-automatic solution

already_exposed solution is pretty good when you mix hand-written modules with the Py++ generated ones. It doesn’t work/scale for “true” multi-module development. This is exactly the reason why Py++ offers “semi automatic” solution.

For every exposed module, Py++ generates “exposed_decl.pypp.txt” file. This file contains the list of all parsed declarations and whether they were included or excluded. Later, when you work on another module, you can tell Py++ that the current module depends on the previously generated one. Py++ will load “exposed_decl.pypp.txt” file and update the declarations.

Usage example:

mb = module_builder_t( ... )
mb.register_module_dependency( <<<other module generated code directory>>> )

Caveat

You should import module “core”, before “png”. Boost.Python requires definition of any base class to be exposed\registered before a derive one.