我用C ++编写了一个类的一部分,我希望能够将它与Python GUI一起使用,所以我使用Boost.Python来尝试使它变得简单。我遇到的问题是,在遵循他们的指南(http://www.boost.org/doc/libs/1_55_0/libs/python/doc/tutorial/doc/html/python/exposing.html)时,每当我运行 bjam 时,我都会收到以下异常:
PacketWarrior/pcap_ext.cc:21:5: error: too few template arguments for class template 'class_'
显然,我在抱怨他们声称他们声称是'class_'模板函数的可选参数,但我无法弄清楚原因。我假设这是一个编译器问题,但我不知道如何解决它。我正在运行OS X 10.9并使用darwin作为默认工具集,但GCC会抛出相同的错误。如果有帮助的话,我的Boost版本是1_55_0。
类头文件(省略了标头保护):
#include <queue>
#include "pcap.h"
#include "Packet.h"
class PacketEngine {
public:
PacketEngine();
~PacketEngine();
const char** getAvailableDevices(char *error_buf);
bool selectDevice(const char* dev);
Packet getNextPacket();
private:
char *selected_device;
char **devices;
int num_devices;
std::queue<Packet> packet_queue;
};
cc文件包含对Boost.Python和我的类的引用:
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include "PacketEngine.h"
BOOST_PYTHON_MODULE(pcap_ext) {
using namespace boost::python;
class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine::getAvailableDevices);
}
我的bjam文件(省略了相关的部分和注释):
use-project boost : ../../../Downloads/boost_1_55_0 ;
project
: requirements <library>/boost/python//boost_python
<implicit-dependency>/boost//headers
: usage-requirements <implicit-dependency>/boost//headers
;
python-extension pcap_ext : PacketWarrior/pcap_ext.cc ;
install convenient_copy
: pcap_ext
: <install-dependencies>on <install-type>SHARED_LIB <install-type>PYTHON_EXTENSION
<location>.
;
local rule run-test ( test-name : sources + )
{
import testing ;
testing.make-test run-pyd : $(sources) : : $(test-name) ;
}
run-test pcap : pcap_ext pcap.py ;
非常感谢有关如何规避此异常的任何想法!我查看了添加可选参数的明显路线,但我认为它们与我的项目无关。 class_定义可以在这里找到:
http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/class.html
答案 0 :(得分:1)
简而言之,包括:
boost/python.hpp
:Boost.Python方便的头文件。boost/python/class.hpp
:定义boost::python::class_
。当前包含的头文件声明class_
,没有来自def_visitor.hpp
的默认模板参数。
此外,尝试直接公开PacketEngine::getAvailableDevices()
可能会出现问题:
char*
参数,但字符串在Python中是不可变的。const char**
的类型。对于Python用户来说,期望PacketEngine.getAvailableDevices()
返回包含Python str
的可迭代类型,或者在出错时抛出异常可能是合理的。这可以通过编写委托给原始函数的辅助函数或辅助函数以非侵入方式完成,但是以PacketEngine.getAvailableDevices()
的形式暴露给Python。
以下是基于原始代码的完整示例:
#include <exception> // std::runtime_error
#include <boost/python.hpp>
namespace {
const char* devices_str[] = {
"device A",
"device B",
"device C",
NULL
};
} // namespace
class PacketEngine
{
public:
PacketEngine() : devices(devices_str) {}
const char** getAvailableDevices(char *error_buf)
{
// Mockup example to force an error on second call.
static bool do_error = false;
if (do_error)
{
strcpy(error_buf, "engine not responding");
}
do_error = true;
return devices;
}
private:
const char **devices;
};
/// @brief Auxiliary function for PacketEngine::getAvailableDevices that
/// provides a more Pythonic API. The original function accepts a
/// char* and returns a const char**. Both of these types are
/// difficult to use within Boost.Python, as strings are immutable
/// in Python, and Boost.Python is focused to providing
/// interoperability to C++, so the const char** type has no direct
/// support.
boost::python::list PacketEngine_getAvailableDevices(PacketEngine& self)
{
// Get device list and error from PacketEngine.
char error_buffer[256] = { 0 };
const char** devices = self.getAvailableDevices(error_buffer);
// On error, throw an exception. Boost.Python will catch it and
// convert it to a Python's exceptions.RuntimeError.
if (error_buffer[0])
{
throw std::runtime_error(error_buffer);
}
// Convert the c-string array to a list of Python strings.
namespace python = boost::python;
python::list device_list;
for (unsigned int i = 0; devices[i]; ++i)
{
const char* device = devices[i];
device_list.append(python::str(device, strlen(device)));
}
return device_list;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine_getAvailableDevices);
}
交互式使用:
>>> import example
>>> engine = example.PacketEngine()
>>> for device in engine.getAvailableDevices():
... print device
...
device A
device B
device C
>>> devices = engine.getAvailableDevices()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: engine not responding