Sign in to follow this  

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

This topic is 2951 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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

This topic is 2951 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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