C++中的Python回调
我有一个C++类
class EventHandler {
virtual long readFromDaemon(void *buf, size_t count) = 0;
};
还有一个使用这个类的Python程序
class Handler(EventHandler):
def readFromDaemon(self, buf, count):
...
C++代码调用了EventHandler::readFromDaemon()。SWIG会转换参数并调用Python的readFromDaemon():
long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
...
swig::SwigVar_PyObject obj0;
obj0 = SWIG_NewPointerObj(SWIG_as_voidptr(buf), SWIGTYPE_p_void, 0 );
swig::SwigVar_PyObject obj1;
obj1 = SWIG_From_size_t(static_cast< size_t >(count));
...
swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar((char *)"readFromDaemon");
swig::SwigVar_PyObject result = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name ,(PyObject *)obj0,(PyObject *)obj1, NULL);
我想把(void *buf, size_t count)转换成PyBytesObject
def readFromDaemon(self, buf):
# buf is bytes
但是我没有找到实现这个的办法。
%typemap(in) (void *buf, size_t count) { ... } does not help.
2 个回答
0
我在这里的另一个话题中找到了一个决定。
%typemap(directorin) (void *buf, size_t count) {
$input = PyBytes_FromStringAndSize(static_cast<char*>($1), $2);
}
但是还有另一个问题。内存缓冲区是从C++复制到Python对象的。那么如何将字节从Python对象复制回C++呢?自从Python 2.7开始有了memoryview,但对于2.6及更早的版本该怎么办呢?
这是C++代码,它调用了EventHandler::readFromDaemon,为了更清楚。
std::vector<char> buf(maxlen);
long cnt = m_evt_handler->readFromDaemon(&buf[0], buf.size());
这里是现在的包装代码。
long SwigDirector_EventHandler::readFromDaemon(void *buf, size_t count) {
long c_result;
swig::SwigVar_PyObject obj0;
{
obj0 = PyBytes_FromStringAndSize(static_cast<char*>(buf), count);
}
...
1
这里有一个关于如何读取和写入C++数据缓冲区的例子。在我的例子中,使用了以下C++函数:
void read(void* buf, size_t count);
void write(const void* buf, size_t count);
SWIG接口文件:
%module x
%include <exception.i>
// Handle an input-only (const) buffer.
// A single Python input is translated into a buffer and a size.
%typemap(in) (const void *buf, size_t count) (Py_ssize_t tmp) %{
if(PyBytes_AsStringAndSize($input,(char**)&$1,&tmp) == -1)
return NULL;
$2 = tmp;
%}
// Handle an output-only (non-const) buffer.
// The single integer Python input is allocated in a temporary
// buffer and the pointer and its size are created as C++ parameters.
%typemap(in,numinputs=1) (void *buf, size_t count) (long tmp) %{
if(!PyLong_Check($input))
SWIG_exception(SWIG_TypeError,"expected integer");
tmp = PyLong_AsLong($input);
if(tmp < 1 || tmp > 65535)
SWIG_exception(SWIG_ValueError,"expected value 1-65535");
$2 = tmp;
$1 = new char[$2];
%}
// The pair of output arguments are translated into a single
// Python bytes object and appended to any existing return value.
%typemap(argout) (void *buf, size_t count) (PyObject* po) %{
po = PyBytes_FromStringAndSize((char*)$1,$2);
$result = SWIG_Python_AppendOutput($result,po);
%}
// Free the temporary buffer created in the "in" typemap.
%typemap(freearg) (void* buf, size_t count) %{
delete $1;
%}
// SWIG will wrap these two functions prototypes.
void read(void* buf, size_t count);
void write(const void* buf, size_t count);
// Implementation
%{
#include <iostream>
void write(const void* buf, size_t count)
{
char* tmp = (char*)buf;
for(size_t i = 0; i < count; i++)
std::cout << i << ": " << (int)tmp[i] << std::endl;
}
void read(const void* buf, size_t count)
{
char* tmp = (char*)buf;
for(size_t i = 0; i < count; i++)
tmp[i] = (char)i;
}
%}
演示:
>>> import x
>>> x.read(10)
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'
>>> x.read(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: expected value 1-65535
>>> x.read(1000000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: expected value 1-65535
>>> x.write(b'abcdefg')
0: 97
1: 98
2: 99
3: 100
4: 101
5: 102
6: 103
>>> x.write(12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected bytes, int found