Sign in to follow this  
kloffy

[Py++] Automatically register boost::shared_ptr to python

Recommended Posts

I would like Py++ to automatically insert "boost::pytheon::register_ptr_to_python< boost::shared_ptr<T> >()" for all classes that it exposes. I want this, because I heavily rely on boost::shared_ptr in my project and I want to be able to convert those to boost::python::object in order to pass them to the python interpreter. Contrary to my expectation, this has proven a bit complicated. Ultimately, I came up with something that works, but I'm wondering why there isn't an easier way of doing it. Here's what I do at the moment:
# Register Smart Pointers
find_classes = code_creators.creator_finder.find_by_class_instance
class_creators = find_classes(what=code_creators.class_t, where=mb.code_creator, recursive=True)
for creator in class_creators:
    creator.adopt_creator(code_creators.smart_pointer_registrator_t('boost::shared_ptr',creator))
Do you see any problems with this? Has anyone tried something like this before?

Share this post


Link to post
Share on other sites
Well, I can answer my first question myself. I found one problem, the previous code can create duplicate "bp::register_ptr_to_python< boost::shared_ptr<T> >()" statements for certain classes. (For example if Py++ sees a class having a boost::shared_ptr<T> as a member...)

Here's a version that fixes the problem. This is a complete Py++ module builder script to provide some context:

#! /usr/bin/python

import os
import sys
sys.path.append( '../../../..' )
from pyplusplus import module_builder
from pyplusplus import code_creators

mb = module_builder.module_builder_t(
files=["classes.h"],
include_paths=["C:\\Progra~1\\boost\\boost_1_39"],
gccxml_path="C:\\Progra~1\\gccxml\\0.9\\bin\\gccxml.exe",
working_directory='.'
)

#Now it is the time to give a name to our module
mb.build_code_creator( module_name='classes' )

#Register Smart Pointers
find_classes = code_creators.creator_finder.find_by_class_instance
class_creators = find_classes(what=code_creators.class_t, where=mb.code_creator, recursive=True)
smart_pointer_registrators = find_classes(what=code_creators.smart_pointer_registrator_t, where=mb.code_creator, recursive=True)
for creator in [creator for creator in class_creators if creator not in [registrator.class_creator for registrator in smart_pointer_registrators]]:
creator.adopt_creator(code_creators.smart_pointer_registrator_t('boost::shared_ptr',creator))

#And finally we can write code to the disk
mb.write_module( os.path.join( os.path.abspath('.'), 'classes.inl' ) )





And here are the sources I used for testing:

classes.h
#pragma once
#ifndef CLASSES_H
#define CLASSES_H

#if defined(__GCCXML__)
#define _HAS_TR1 0
#endif

#include <iostream>

#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

class Class1
{
public:
typedef boost::shared_ptr<Class1> ptr;

int x;
int y;

Class1(int _x, int _y):x(_x),y(_y){}
};

class Class2
{
public:
typedef boost::shared_ptr<Class2> ptr;

Class1::ptr c;

Class2(const Class1::ptr& _c):c(_c){}

virtual void f(){ std::cout << "Class2()" << std::endl; }
};

#endif





classes.inl (generated)
// This file has been generated by Py++.

#include "boost/python.hpp"

#include "classes.h"

namespace bp = boost::python;

struct Class2_wrapper : Class2, bp::wrapper< Class2 > {

Class2_wrapper(Class2 const & arg )
: Class2( arg )
, bp::wrapper< Class2 >(){
// copy constructor

}

Class2_wrapper(::boost::shared_ptr< Class1 > const & _c )
: Class2( _c )
, bp::wrapper< Class2 >(){
// constructor

}

virtual void f( ) {
if( bp::override func_f = this->get_override( "f" ) )
func_f( );
else
this->Class2::f( );
}


void default_f( ) {
Class2::f( );
}

};

BOOST_PYTHON_MODULE(classes){
{ //::Class1
typedef bp::class_< Class1 > Class1_exposer_t;
Class1_exposer_t Class1_exposer = Class1_exposer_t( "Class1", bp::init< int, int >(( bp::arg("_x"), bp::arg("_y") )) );
bp::scope Class1_scope( Class1_exposer );
Class1_exposer.def_readwrite( "x", &Class1::x );
Class1_exposer.def_readwrite( "y", &Class1::y );
bp::register_ptr_to_python< boost::shared_ptr< Class1 > >(); //All that trouble just to get this!
}

{ //::Class2
typedef bp::class_< Class2_wrapper > Class2_exposer_t;
Class2_exposer_t Class2_exposer = Class2_exposer_t( "Class2", bp::init< boost::shared_ptr< Class1 > const & >(( bp::arg("_c") )) );
bp::scope Class2_scope( Class2_exposer );
bp::implicitly_convertible< boost::shared_ptr< Class1 > const &, Class2 >();
{ //::Class2::f

typedef void ( ::Class2::*f_function_type )( ) ;
typedef void ( Class2_wrapper::*default_f_function_type )( ) ;

Class2_exposer.def(
"f"
, f_function_type(&::Class2::f)
, default_f_function_type(&Class2_wrapper::default_f) );

}
Class2_exposer.def_readwrite( "c", &Class2::c );
bp::register_ptr_to_python< boost::shared_ptr< Class2 > >(); //All that trouble just to get this!
}
}





main.cpp
#include "classes.inl"

namespace py = boost::python;

int main(int argc, char **argv)
{
int exit = EXIT_SUCCESS;

Py_Initialize();

try
{
initclasses();

Class1::ptr o1 = boost::make_shared<Class1>(1,2);

py::object main_module = py::import("__main__");
py::object main_namespace = main_module.attr("__dict__");

py::object po1(o1);
main_namespace["o1"] = po1;

py::object ignored = py::exec(
"from classes import *\n"
"class Class3(Class2):\n"
" def __init__(self,c):\n"
" Class2.__init__(self,c)\n"
" def f(self):\n"
" print \"Class3()\"\n"
"o3 = Class3(o1)\n",
main_namespace, main_namespace
);

boost::shared_ptr<Class2> o3 = py::extract< boost::shared_ptr<Class2> >(main_namespace["o3"]);

std::cout << "Class1("<< py::extract<int>(po1.attr("x")) << "," << py::extract<int>(po1.attr("y")) << ")" << std::endl;

o3->f();
}
catch(const py::error_already_set&)
{
PyErr_Print();
exit = EXIT_FAILURE;
}

Py_Finalize();

return exit;
}




Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this