0
votes

I have a C++ library with 2 classes defined in it: t_foo_base and t_foo. t_foo is derived from t_foo_base. Both of them implement a virtual function text. This function returns a string with the name of the current class. I use boost.python to generate a wrapper for this library and for this classes.

I import this library in a python script. In this script I implemented a function. This function takes one parameter and call the function "text()" on it.

Now i import this python script in a C++ application. I use boost.python again. I get the function "test_function" from the python script and call it this way:

t_foo_base foo_base;
test_function( foo_base );

t_foo foo;
test_function( foo );

t_foo_base* foo_base_tpr = new t_foo_base;
test_function( *foo_base_tpr );

t_foo_base* foo_ptr = new t_foo;
test_function( *foo_ptr );

The outpur is:

t_foo_base

t_foo

t_foo_base

t_foo_base

I would expact the 4th line of the output to be "t_foo", not "t_foo_base".

It seems, that passing a derived object by its base class pointer "cuts away" all the features of the derived object. Is there a way to do fix this problem?

I am using Visual Studio 2013, Python 3.4 and Boost 1.56.0.

This is code from the C++ library:

-- file t_foo.h --

class __declspec( dllexport ) t_foo_base
{
public:
    t_foo_base(){};

    virtual ~t_foo_base(){}

    virtual std::string text( void ) const { return( "t_foo_base" ); };
};


class __declspec( dllexport ) t_foo: public t_foo_base
{
public:
    t_foo(){};

    virtual std::string text( void ) const override { return( "t_foo" ); };
};

-- file t_foo.cpp --

#include "t_foo.h"

#include <boost/python.hpp>

using namespace boost::python;

BOOST_PYTHON_MODULE( ex_three_lib )
{
    class_< t_foo_base >( "t_foo_base" )
        .def( "text", &t_foo_base::text );
    class_< t_foo, bases< t_foo_base > >( "t_simple_callback" )
        .def( "text", &t_foo::text );
}

This is the python script:

import ex_three_lib

def test_function( item ):
    print( item.text() )

print( "no function call here" )

This is the c++ application:

void
init_module_ex_three_lib
(
    void
);


boost::python::object
import_python_object( const std::string& p_name, const std::string& p_path, boost::python::api::object& p_global )
{
    using namespace boost::python;
    try
    {
        dict locals;
        locals[ "modulename" ] = p_name;
        locals[ "path" ] = p_path;
        exec
        (
            "import imp\n"
            "newmodule = imp.load_module( modulename, open( path ), path,( 'py', 'U', imp.PY_SOURCE ) )\n",
            p_global,
            locals 
        );
        return locals[ "newmodule" ];
    }
    catch( boost::python::error_already_set const & )
    {
        if( PyErr_ExceptionMatches( PyExc_ZeroDivisionError ) )
        {
            assert( false );
        }
        else
        {
            PyErr_Print();
        }
    }
}




int
main
(
void
)
{
    Py_Initialize();

    init_module_ex_three_lib();

    try
    {
        boost::python::object main_module = boost::python::import( "__main__" );
        boost::python::object main_namespace = main_module.attr( "__dict__" );

        {
            boost::python::object script = import_python_object
            (
                "ex_one_script",
                "ex_three_script.py",
                main_namespace
            );

            boost::python::object test_function = script.attr( "test_function" );
            
            t_foo_base foo_base;
            test_function( foo_base );

            t_foo foo;
            test_function( foo );

            t_foo_base* foo_base_tpr = new t_foo_base;
            test_function( *foo_base_tpr );

            t_foo_base* foo_ptr = new t_foo;
            test_function( *foo_ptr );
        }
    }
    catch( boost::python::error_already_set const & )
    {
        if( PyErr_ExceptionMatches( PyExc_ZeroDivisionError ) )
        {
            assert( false );
        }
        else
        {
            PyErr_Print();
        }
    }
}
1
Well, this call will not work as you expect if test_function is taking test_foo objects by value: test_function( *foo_ptr ); Polymorphism only works if passing a pointer or reference, not a value. Since I don't know boost::python, maybe you can confirm this? - PaulMcKenzie
Look at this simple C++ program to show you what I am referring to: ideone.com/M326Pz Note that even if the program creates a derived object from a base pointer, the function being called is taking a value, not a pointer or reference. So you need to check if this is what is going on with what you're attempting to do. - PaulMcKenzie

1 Answers

1
votes

You should use boost::ref(...) here. This will basically convert any value/pointer to its reference. Also see Calling Python Functions and Methods

        t_foo_base foo_base;
        test_function( boost::ref(foo_base) );

        t_foo foo;
        test_function( boost::ref(foo) );

        t_foo_base* foo_base_tpr = new t_foo_base;
        test_function( boost::ref(*foo_base_tpr) );

        t_foo_base* foo_ptr = new t_foo;
        test_function( boost::ref(*foo_ptr) );

->

t_foo_base
t_foo
t_foo_base
t_foo