将Python类实例传递给C ++函数

时间:2014-05-03 04:18:30

标签: boost-python

我有一个用C ++编写的内部库,我正在努力扩展到Python。我开始考虑使用Boost.Python来完成这项任务,但我愿意接受替代方案。

目前我有一个需要接受Python类实例的C ++函数,然后使用该对象的方法来执行某些任务。这个想法是让Python用户永远不需要处理C ++。他们应该从我将提供的Python模板/示例类创建这个Python对象,使用预设的方法名称,我可以假设它在我的C ++库中。

暴露给Python用户的界面如下所示:

class Foo(object):

    def __init__(self, args):
        """create an instance of this class with instance-specific attributes"""

    def Bar1(self, a, b, c):
        """do something with the given integers a, b and c"""
        pass

    def Bar2(self, a, b, c):
        """do something else with the given integers a, b and c"""
        pass

import mylib

cheese = mylib.Wine()
cheese.do_something(Foo)

在C ++中,相应的代码如下所示:

#include <boost/python.h>
#include <Python.h>

class Wine {

public:  

    Wine() {};

    ~Wine() {};

    static void do_something(boost::python::object *Foo) {

        int a = 1;
        int b = 2;
        int c = 3;

        Foo->attr("Bar1")(a, b, c);
        Foo->attr("Bar2")(a, b, c);
    };
};

BOOST_PYTHON_MODULE(mylib)
{
    using namespace boost::python;
    class_<Wine>("Wine")
        .def("do_something", &Wine::do_something);
};

我已经成功编译了这段代码并验证了名为Wine的C ++类实际上是暴露给Python的,我可以访问它的成员函数。如果我写一个名为“greet()”的成员函数只返回“Hello,world!”,它就能完美运行。

我需要强调传递Foo实例的重要性。我没有什么可以简单地将Foo模块导入C ++代码并在C ++中创建Foo实例。我想从Python用户接收的对象具有我需要使用的特定于实例的属性,而不是类本身。

问题是我无法弄清楚如何将Python实例传递给do_something,因此它将作为可调用的boost :: python :: object出现在C ++中。上面的代码返回以下C ++签名不匹配错误:

Boost.Python.ArgumentError: Python argument types in
    Wine.do_something(Wine, Foo)
did not match C++ signature:
    do_something(boost::python::api::object*)

在网上浏览答案两天没有取得任何进展。似乎有很多关于如何将C ++类传递给Python的信息,但我无法找到相反方向的信息。非常感谢这里的一些指导。

谢谢!

2 个答案:

答案 0 :(得分:3)

初始代码中有两个错误:

  • Boost.Python尝试将两个参数(self和一个Foo实例)传递给只接受一个参数的静态Wine::do_something() C ++函数。要解决此问题,在公开Wine类时,需要通过boost::python::class_::staticmethod()成员函数将Python Wine.do_something()成员函数设置为静态。当作为静态方法公开时,Boost.Python将不再传递self实例参数。
  • 与Python / C API不同,其中指针通常用作对象的句柄(PyObject*),Boost.Python提供了更高级别的表示法boost::python::object,它通常按值传递或参考。在内部,此类与boost::python::handle交互,为PyObject执行智能指针管理。

这是一个基于原始代码的完整Python扩展:

#include <boost/python.hpp>

class Wine
{
public:  

  static void do_something(boost::python::object object)
  {
    int a = 1;
    int b = 2;
    int c = 3;

    object.attr("Bar1")(a, b, c);
    object.attr("Bar2")(a, b, c);
  };
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<Wine>("Wine")
    .def("do_something", &Wine::do_something)
      .staticmethod("do_something")
    ;
};

交互式使用:

>>> class Foo(object):
...     def Bar1(self, a, b, c):
...         print "Bar1", locals()
...     def Bar2(self, a, b, c):
...         print "Bar2", locals()
... 
>>> import example
>>> cheese = example.Wine()
>>> cheese.do_something(Foo())
Bar1 {'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>}
Bar2 {'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>}

答案 1 :(得分:0)

要公开接受Python对象作为参数的方法,您应该使用boost::python::object而不是boost::python::object *

void do_something(boost::python::object Foo)

要公开静态方法,请将其公开为常规函数:def("do_something", Wine::do_something);

import mylib

# method
cheese = mylib.Wine()
foo = Foo()
cheese.do_something(foo)

#static method
foo = Foo()
mylib.do_something(foo)