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 classpng_image_t
, which derives fromcore::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.