没有为C ++类函数指针注册python类

时间:2016-11-02 00:47:15

标签: c++ boost boost-python

我正在尝试在Python中为pika编写一个包装C++类。在pika中,当消息被消费时,有一个名为callback(ch, method, properties, body)的函数。要使用消息,您必须将callback函数放在basic_consume方法中。在我的例子中,我的callback函数驻留在C++代码中,因为C++处理所有必要的事情,然后将回调转移回Consumer类。我想在C++文件中执行每个逻辑,并在这种情况下单独留下Python class

C++:

#include <stdio.h>
#include <boost/python.hpp>
#include <boost/function.hpp>

using namespace std;
using namespace boost::python;


// boost function
void function(object *ch, object *method, object *properties, string body) {
    cout << "INSIDE FUNC" << body << endl;
}       

int main() {
    Py_Initialize(); 
    try {
        boost::function<void(object*, object*, object*, string)> myfunc;
        myfunc = boost::bind(function, _1, _2, _3, _4);
        object a = import("consumer");
        object b = a.attr("A");
        object c = b.attr("callback")(boost::ref(myfunc));
    }
    catch(error_already_set const &) {
        PyErr_Print();
    } 
    return 0;
}

Python: consumer.py

import pika 

class A:
    def __init__(self): 
        cppcallback = None
        self.connect()

    def connect(): 
        connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
        channel = connection.channel()

    def callback(cppdosomething): 
        print "CALLED"
        cppcallback = cppdosomething 
        self.start_consume()

    def start_consume(self): 
        channel.basic_consume(cppcallback, queue="hello_world")
        channel.start_consuming()

但是现在,我收到了这个错误。

  

TypeError:没有为C ++类注册的Python类boost :: function&lt; void(boost :: python :: api :: object *,boost :: python :: api :: object *,boost :: python :: api :: object *,std :: string)&gt;

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

评论

您的示例代码存在一些无关的问题:

  • object b = a.attr("A"); - 获取类类型。您需要运行构造函数来创建A的实例。这意味着a.attr("A")();
  • 在Python脚本中,cppcallback__init__callback函数中的局部变量。在start_consume中它未定义。这应该是一个成员变量,如self.cppcallback
  • 指向object作为回调处理程序的参数是值得商榷的。我认为按价值传递它们很好。

解决方案

Python脚本

我写了一个简化的脚本,模仿你所拥有的:

class A:
    def __init__(self):
        self.handler = None

    def callback(self, handler):
        self.handler = handler
        self.do_something()

    def do_something(self):
        self.handler(1,2,3,"foo")

使用普通功能

这种方法非常简单。

首先使用object创建一个可调用的make_function

然后从我们的脚本,test_module的构造和实例中导入Python A并调用它的callback成员将其作为参数传递给我们的可调用对象。

#include <boost/python.hpp>
namespace bp = boost::python;

void callback_handler(bp::object ch
    , bp::object method
    , bp::object properties
    , std::string const& body)
{
    std::cout << "in handler: " << body << std::endl;
}

int main()
{
    Py_Initialize();
    try {
        bp::object h = bp::make_function(callback_handler);

        bp::object a = bp::import("test_module");
        bp::object b = a.attr("A")(); // Construct instance of A

        b.attr("callback")(h);
    } catch (bp::error_already_set const &) {
        PyErr_Print();
    }
    return 0;
}

控制台输出:

>example_1.exe
in handler: foo

使用boost::function

使用boost::function对象作为回调处理程序有点棘手,因为默认情况下boost::function不支持boost::python。因此,我们首先需要启用对boost::function的支持,如this answer Tanner Sansbury中所述。

NB :此片段需要先包括boost/python.hpp

// ============================================================================
// Enable support for boost::function
// See https://stackoverflow.com/a/18648366/3962537
// ----------------------------------------------------------------------------
#include <boost/function.hpp>
#include <boost/function_types/components.hpp>
// ----------------------------------------------------------------------------
namespace boost { namespace python { namespace detail {
// ----------------------------------------------------------------------------
// get_signature overloads must be declared before including
// boost/python.hpp.  The declaration must be visible at the
// point of definition of various Boost.Python templates during
// the first phase of two phase lookup.  Boost.Python invokes the
// get_signature function via qualified-id, thus ADL is disabled.
// ----------------------------------------------------------------------------
/// @brief Get the signature of a boost::function.
template <typename Signature>
inline typename boost::function_types::components<Signature>::type
get_signature(boost::function<Signature>&, void* = 0)
{
    return typename boost::function_types::components<Signature>::type();
}
// ----------------------------------------------------------------------------
}}} // namespace boost::python::detail
// ============================================================================

其余与第一种情况非常相似。

#include <iostream>
#include <boost/python.hpp>
namespace bp = boost::python;

void callback_handler(bp::object ch
    , bp::object method
    , bp::object properties
    , std::string const& body
    , std::string const& extra)
{
    std::cout << "in handler: " << body << extra << std::endl;
}

int main()
{
    Py_Initialize();
    try {
        typedef boost::function<void(bp::object, bp::object, bp::object, std::string)> handler_fn;
        handler_fn my_handler(boost::bind(callback_handler, _1, _2, _3, _4, " bar"));
        bp::object h = bp::make_function(my_handler);

        bp::object a = bp::import("test_module");
        bp::object b = a.attr("A")();

        b.attr("callback")(h);
    } catch (bp::error_already_set const &) {
        PyErr_Print();
    }
    return 0;
}

控制台输出:

>example_2.exe
in handler: foo bar