【问题标题】:Passing a polymorphic object from C++ to a python function将多态对象从 C++ 传递给 python 函数
【发布时间】:2014-08-29 15:53:02
【问题描述】:

我有一个 C++ 库,其中定义了 2 个类:t_foo_baset_foo。 t_foo 派生自 t_foo_base。它们都实现了一个虚函数text。此函数返回一个带有当前类名称的字符串。我使用 boost.python 为这个库和这个类生成一个包装器。

我在 python 脚本中导入这个库。在这个脚本中,我实现了一个函数。该函数接受一个参数并在其上调用函数“text()”。

现在我将这个 python 脚本导入到 C++ 应用程序中。我再次使用 boost.python。我从 python 脚本中得到函数“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 );

输出是:

t_foo_base

t_foo

t_foo_base

t_foo_base

我会将输出的第 4 行解释为“t_foo”,而不是“t_foo_base”。

看起来,通过基类指针传递派生对象会“删除”派生对象的所有特性。有没有办法解决这个问题?

我正在使用 Visual Studio 2013、Python 3.4 和 Boost 1.56.0。

这是来自 C++ 库的代码:

-- 文件 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" ); };
};

-- 文件 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 );
}

这是python脚本:

import ex_three_lib

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

print( "no function call here" )

这是 C++ 应用程序:

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();
        }
    }
}

【问题讨论】:

  • 好吧,如果test_function 按值获取test_foo 对象,则此调用将无法正常工作:test_function( *foo_ptr ); 多态性仅在传递指针或引用而不是值时才有效。由于我不知道 boost::python,也许你可以确认一下?
  • 看看这个简单的 C++ 程序,向您展示我所指的内容:ideone.com/M326Pz 请注意,即使程序从基指针创建派生对象,被调用的函数也会获取一个值,不是指针或引用。所以你需要检查这是否是你正在尝试做的事情。

标签: python c++ python-3.x boost boost-python


【解决方案1】:

你应该在这里使用boost::ref(...)。这基本上会将任何值/指针转换为其引用。另见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

【讨论】:

  • 这就是我想要的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 2015-02-01
  • 1970-01-01
相关资源
最近更新 更多