如何使用 boost.python 将预填充的 "unsigned char*" 缓冲区传递给 C++ 方法?
How do I pass a pre-populated "unsigned char*" buffer to a C++ method using boost.python?
我有一个 C++ class,它有一个成员函数,它接受一个 unsigned char* 缓冲区和一个 unsigned int 长度作为参数并对它们进行操作。我已经用 Boost::Python 包装了这个 class 并且想将一个预先填充的缓冲区从 Python 脚本传递到 class。 Python 端缓冲区是用 struct.pack 创建的。我不知道如何使参数类型匹配并不断得到 Boost.Python.ArgumentError.
include/Example.h
#ifndef EXAMPLECLASS_H_
#define EXAMPLECLASS_H_
#include <cstdio>
class ExampleClass
{
public:
ExampleClass() {}
virtual ~ExampleClass() {}
void printBuffer(unsigned char* buffer, unsigned int length)
{
for (unsigned int i = 0; i < length; ++i)
{
printf("%c", buffer[i]);
}
printf("\n");
}
};
#endif
src/example.cpp
#include "Example.h"
int main(int argc, char** argv)
{
unsigned char buf[4];
buf[0] = 0x41;
buf[1] = 0x42;
buf[2] = 0x43;
buf[3] = 0x44;
ExampleClass e;
e.printBuffer(buf, 4);
return 0;
}
src/Example_py.cpp
#include <boost/python.hpp>
#include "Example.h"
using namespace boost::python;
BOOST_PYTHON_MODULE(example_py)
{
class_<ExampleClass>("ExampleClass")
.def("printBuffer", &ExampleClass::printBuffer)
;
}
scripts/example.py
#!/usr/bin/env python
import example_py
import struct
import ctypes
buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
print 'python:'
print buf
e = example_py.ExampleClass()
print 'c++:'
print e.printBuffer(ctypes.cast(ctypes.c_char_p(buf), ctypes.POINTER(ctypes.c_ubyte)), len(buf))
CMakeLists.txt(不完整)
include_directories(
include
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
)
add_library(example_py
src/Example_py.cpp
)
target_link_libraries(example_py ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})
set_target_properties(example_py PROPERTIES PREFIX "")
add_executable(example src/example.cpp)
target_link_libraries(example example_py)
输出
$ ./example
ABCD
$ ./scripts/example.py
python: ABCD
c++:
Traceback (most recent call last):
File "/home/dustingooding/example/scripts/example.py", line 13, in <module>
print 'c++:', e.printBuffer(ctypes.cast(ctypes.c_char_p(buf), ctypes.POINTER(ctypes.c_ubyte)), len(buf))
Boost.Python.ArgumentError: Python argument types in
ExampleClass.printBuffer(ExampleClass, LP_c_ubyte, int)
did not match C++ signature:
printBuffer(ExampleClass {lvalue}, unsigned char*, unsigned int)
我尝试了多种不同的方法(直接传递 'buf',将 'buf' 作为 ctypes.c_char_p 传递,创建一个 ctypes.ubyte 数组并用'buf' 的内容并传递它),但 none 似乎有效。
我不明白为什么 'LP_c_ubyte' 和 'unsigned char*' 不匹配。
编辑
这是一个带有现成代码库的 Github 项目。随意使用它。我添加了@Tanner 的修复程序。 https://github.com/dustingooding/boost_python_ucharp_example
python documentation 在 基本数据类型 一章中列出了以下内容:
class ctypes.c_char_p
Represents the C char *
datatype when it points to a zero-terminated string. For a general character pointer
that may also point to binary data, POINTER(c_char)
must be used. The
constructor accepts an integer address, or a string.
表明您可能应该使用 c_char_p
类型。如果您使用 POINTER()
函数,这将是 LP_c_char_p
.
类型
LP_c_ubyte /* corresponds to */ unsigned char;
你可能应该使用
LP_c_char_p /* which corresponds to */ char *;
更新:
我已经更正了上面的类型。另外:我不是 python 专家,所以我可能有误。还有this answer.
可能值得考虑将 Pythonic 辅助函数作为 ExampleClass.printBuffer
方法公开给 Python,委托给 c-ish ExampleClass::printBuffer
成员函数。例如,这将允许 Python 用户调用:
import example
import struct
buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
e.printBuffer(buf)
而不是要求用户执行正确的 ctypes
转换和调整大小。
struct.pack()
method returns a str
object in Python2 and a bytes
object in Python3, so the auxiliary C++ function would need to populate a continuous block of memory with the elements of from either str
or bytes
. The boost::python::stl_input_iterator
可以提供一种方便的方法来从 Python 对象(例如 str
或 bytes
构造 C++ 容器,例如 std::vector<char>
].唯一奇怪的是 stl_input_iterator
期望 Python 类型支持可迭代协议,而 str
不支持。但是,内置的 iter()
Python 方法可用于创建可迭代对象。
/// @brief Auxiliary function used to allow a Python iterable object with char
/// elements to be passed to ExampleClass.printBuffer().
void example_class_print_buffer_wrap(
ExampleClass& self,
boost::python::object py_buffer)
{
namespace python = boost::python;
// `str` objects do not implement the iterator protcol (__iter__),
// but do implement the sequence protocol (__getitem__). Use the
// `iter()` builtin to create an iterator for the buffer.
// >>> __builtins__.iter(py_buffer)
python::object locals(python::borrowed(PyEval_GetLocals()));
python::object py_iter = locals["__builtins__"].attr("iter");
python::stl_input_iterator<char> begin(
py_iter(py_buffer)), end;
// Copy the py_buffer into a local buffer with known continguous memory.
std::vector<char> buffer(begin, end);
// Cast and delegate to the printBuffer member function.
self.printBuffer(
reinterpret_cast<unsigned char*>(&buffer[0]),
buffer.size());
}
创建辅助函数后,只需将其公开为ExampleClass.printBuffer
方法即可:
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<ExampleClass>("ExampleClass")
.def("printBuffer", &example_class_print_buffer_wrap)
;
}
这里有一个完整的例子demonstrating这种方法:
#include <cstdio>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
// Mocks...
/// @brief Legacy class that cannot be changed.
class ExampleClass
{
public:
void printBuffer(unsigned char* buffer, unsigned int length)
{
for (unsigned int i = 0; i < length; ++i)
{
printf("%c", buffer[i]);
}
printf("\n");
}
};
/// @brief Auxiliary function used to allow a Python iterable object with char
/// elements to be passed to ExampleClass.printBuffer().
void example_class_print_buffer_wrap(
ExampleClass& self,
boost::python::object py_buffer)
{
namespace python = boost::python;
// `str` objects do not implement the iterator protcol (__iter__),
// but do implement the sequence protocol (__getitem__). Use the
// `iter()` builtin to create an iterator for the buffer.
// >>> __builtins__.iter(py_buffer)
python::object locals(python::borrowed(PyEval_GetLocals()));
python::object py_iter = locals["__builtins__"].attr("iter");
python::stl_input_iterator<char> begin(
py_iter(py_buffer)), end;
// Copy the py_buffer into a local buffer with known continguous memory.
std::vector<char> buffer(begin, end);
// Cast and delegate to the printBuffer member function.
self.printBuffer(
reinterpret_cast<unsigned char*>(&buffer[0]),
buffer.size());
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<ExampleClass>("ExampleClass")
.def("printBuffer", &example_class_print_buffer_wrap)
;
}
交互使用:
>>> import example
>>> import struct
>>> buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
>>> print 'python:', buf
python: ABCD
>>> e = example.ExampleClass()
>>> e.printBuffer(buf)
ABCD
我有一个 C++ class,它有一个成员函数,它接受一个 unsigned char* 缓冲区和一个 unsigned int 长度作为参数并对它们进行操作。我已经用 Boost::Python 包装了这个 class 并且想将一个预先填充的缓冲区从 Python 脚本传递到 class。 Python 端缓冲区是用 struct.pack 创建的。我不知道如何使参数类型匹配并不断得到 Boost.Python.ArgumentError.
include/Example.h
#ifndef EXAMPLECLASS_H_
#define EXAMPLECLASS_H_
#include <cstdio>
class ExampleClass
{
public:
ExampleClass() {}
virtual ~ExampleClass() {}
void printBuffer(unsigned char* buffer, unsigned int length)
{
for (unsigned int i = 0; i < length; ++i)
{
printf("%c", buffer[i]);
}
printf("\n");
}
};
#endif
src/example.cpp
#include "Example.h"
int main(int argc, char** argv)
{
unsigned char buf[4];
buf[0] = 0x41;
buf[1] = 0x42;
buf[2] = 0x43;
buf[3] = 0x44;
ExampleClass e;
e.printBuffer(buf, 4);
return 0;
}
src/Example_py.cpp
#include <boost/python.hpp>
#include "Example.h"
using namespace boost::python;
BOOST_PYTHON_MODULE(example_py)
{
class_<ExampleClass>("ExampleClass")
.def("printBuffer", &ExampleClass::printBuffer)
;
}
scripts/example.py
#!/usr/bin/env python
import example_py
import struct
import ctypes
buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
print 'python:'
print buf
e = example_py.ExampleClass()
print 'c++:'
print e.printBuffer(ctypes.cast(ctypes.c_char_p(buf), ctypes.POINTER(ctypes.c_ubyte)), len(buf))
CMakeLists.txt(不完整)
include_directories(
include
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
)
add_library(example_py
src/Example_py.cpp
)
target_link_libraries(example_py ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})
set_target_properties(example_py PROPERTIES PREFIX "")
add_executable(example src/example.cpp)
target_link_libraries(example example_py)
输出
$ ./example
ABCD
$ ./scripts/example.py
python: ABCD
c++:
Traceback (most recent call last):
File "/home/dustingooding/example/scripts/example.py", line 13, in <module>
print 'c++:', e.printBuffer(ctypes.cast(ctypes.c_char_p(buf), ctypes.POINTER(ctypes.c_ubyte)), len(buf))
Boost.Python.ArgumentError: Python argument types in
ExampleClass.printBuffer(ExampleClass, LP_c_ubyte, int)
did not match C++ signature:
printBuffer(ExampleClass {lvalue}, unsigned char*, unsigned int)
我尝试了多种不同的方法(直接传递 'buf',将 'buf' 作为 ctypes.c_char_p 传递,创建一个 ctypes.ubyte 数组并用'buf' 的内容并传递它),但 none 似乎有效。
我不明白为什么 'LP_c_ubyte' 和 'unsigned char*' 不匹配。
编辑
这是一个带有现成代码库的 Github 项目。随意使用它。我添加了@Tanner 的修复程序。 https://github.com/dustingooding/boost_python_ucharp_example
python documentation 在 基本数据类型 一章中列出了以下内容:
class ctypes.c_char_p
Represents the C
char *
datatype when it points to a zero-terminated string. For a general character pointer that may also point to binary data,POINTER(c_char)
must be used. The constructor accepts an integer address, or a string.
表明您可能应该使用 c_char_p
类型。如果您使用 POINTER()
函数,这将是 LP_c_char_p
.
类型
LP_c_ubyte /* corresponds to */ unsigned char;
你可能应该使用
LP_c_char_p /* which corresponds to */ char *;
更新: 我已经更正了上面的类型。另外:我不是 python 专家,所以我可能有误。还有this answer.
可能值得考虑将 Pythonic 辅助函数作为 ExampleClass.printBuffer
方法公开给 Python,委托给 c-ish ExampleClass::printBuffer
成员函数。例如,这将允许 Python 用户调用:
import example
import struct
buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
e.printBuffer(buf)
而不是要求用户执行正确的 ctypes
转换和调整大小。
struct.pack()
method returns a str
object in Python2 and a bytes
object in Python3, so the auxiliary C++ function would need to populate a continuous block of memory with the elements of from either str
or bytes
. The boost::python::stl_input_iterator
可以提供一种方便的方法来从 Python 对象(例如 str
或 bytes
构造 C++ 容器,例如 std::vector<char>
].唯一奇怪的是 stl_input_iterator
期望 Python 类型支持可迭代协议,而 str
不支持。但是,内置的 iter()
Python 方法可用于创建可迭代对象。
/// @brief Auxiliary function used to allow a Python iterable object with char
/// elements to be passed to ExampleClass.printBuffer().
void example_class_print_buffer_wrap(
ExampleClass& self,
boost::python::object py_buffer)
{
namespace python = boost::python;
// `str` objects do not implement the iterator protcol (__iter__),
// but do implement the sequence protocol (__getitem__). Use the
// `iter()` builtin to create an iterator for the buffer.
// >>> __builtins__.iter(py_buffer)
python::object locals(python::borrowed(PyEval_GetLocals()));
python::object py_iter = locals["__builtins__"].attr("iter");
python::stl_input_iterator<char> begin(
py_iter(py_buffer)), end;
// Copy the py_buffer into a local buffer with known continguous memory.
std::vector<char> buffer(begin, end);
// Cast and delegate to the printBuffer member function.
self.printBuffer(
reinterpret_cast<unsigned char*>(&buffer[0]),
buffer.size());
}
创建辅助函数后,只需将其公开为ExampleClass.printBuffer
方法即可:
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<ExampleClass>("ExampleClass")
.def("printBuffer", &example_class_print_buffer_wrap)
;
}
这里有一个完整的例子demonstrating这种方法:
#include <cstdio>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
// Mocks...
/// @brief Legacy class that cannot be changed.
class ExampleClass
{
public:
void printBuffer(unsigned char* buffer, unsigned int length)
{
for (unsigned int i = 0; i < length; ++i)
{
printf("%c", buffer[i]);
}
printf("\n");
}
};
/// @brief Auxiliary function used to allow a Python iterable object with char
/// elements to be passed to ExampleClass.printBuffer().
void example_class_print_buffer_wrap(
ExampleClass& self,
boost::python::object py_buffer)
{
namespace python = boost::python;
// `str` objects do not implement the iterator protcol (__iter__),
// but do implement the sequence protocol (__getitem__). Use the
// `iter()` builtin to create an iterator for the buffer.
// >>> __builtins__.iter(py_buffer)
python::object locals(python::borrowed(PyEval_GetLocals()));
python::object py_iter = locals["__builtins__"].attr("iter");
python::stl_input_iterator<char> begin(
py_iter(py_buffer)), end;
// Copy the py_buffer into a local buffer with known continguous memory.
std::vector<char> buffer(begin, end);
// Cast and delegate to the printBuffer member function.
self.printBuffer(
reinterpret_cast<unsigned char*>(&buffer[0]),
buffer.size());
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<ExampleClass>("ExampleClass")
.def("printBuffer", &example_class_print_buffer_wrap)
;
}
交互使用:
>>> import example
>>> import struct
>>> buf = struct.pack('BBBB', 0x41, 0x42, 0x43, 0x44)
>>> print 'python:', buf
python: ABCD
>>> e = example.ExampleClass()
>>> e.printBuffer(buf)
ABCD