我正在尝试包装一个C ++ api,而且我在一些char*
类成员上遇到了障碍。似乎boost-python会自动将char const *
和std::string
类型转换为python对象(基于this answer),但它会在char*
类型上进行调整。这是我得到的错误(在python中):
TypeError: No to_python (by-value) converter found for C++ type: char*
事实证明,这些特定的char *
成员可能应该被声明为char const *
,因为字符串永远不会被更改。
我是boost-python的新手所以也许有一个明显的答案,但是我没有太多运气来搜索这个。
有没有一种简单的方法可以告诉boost-python自动转换这些char*
成员?
(遗憾的是,我无法将char *
的声明更改为char const *
,因为我正在包装的API不受我的控制。)
更新:
好的,我认为我需要添加一个自定义转换器来处理char*
成员。我开始写一篇:
/** to-python convert for char* */
struct c_char_p_to_python_str
{
static PyObject* convert(char* s) {
return incref(object(const_cast<const char*>(s)).ptr());
}
};
// register the QString-to-python converter
to_python_converter<char*, c_char_p_to_python_str>();
不幸的是,这不起作用。这是错误:
error: expected unqualified-id
to_python_converter<char*, c_char_p_to_python_str>();
^
查看docs我可以看到模板args有这个签名:
template <class T, class Conversion, bool convertion_has_get_pytype_member=false>
由于char*
不是一个类,我猜这就是为什么这不起作用。任何人都有一些见解?
UPDATE2:
不。结果to_python_converter
需要在BOOST_PYTHON_MODULE
电话中调用。
我让to_python_converter
工作(进行了一些修改)。我还编写了一个函数来转换表单python并将其注册到converter::registry::push_back
。我可以看到我的to_python代码正在运行,但是from_python代码似乎永远不会运行。
答案 0 :(得分:2)
让我们假设我们正在包装一些第三方API,并将那些指针暴露出来的可怕性放在一边,并将它们从外部捣乱。
这是一个简短的概念证明:
#include <boost/python.hpp>
namespace bp = boost::python;
class example
{
public:
example()
{
text = new char[1];
text[0] = '\0';
}
~example()
{
delete[] text;
}
public:
char* text;
};
char const* get_example_text(example* e)
{
return e->text;
}
void set_example_text(example* e, char const* new_text)
{
delete[] e->text;
size_t n(strlen(new_text));
e->text = new char[n+1];
strncpy(e->text, new_text, n);
e->text[n] = '\0';
}
BOOST_PYTHON_MODULE(so02)
{
bp::class_<example>("example")
.add_property("text", &get_example_text, &set_example_text)
;
}
班级example
拥有text
,负责管理记忆。
我们提供外部getter和setter功能。 getter很简单,只提供对字符串的读访问权限。 setter释放旧字符串,分配适当大小的新内存,并复制数据。
这是python解释器中的一个简单测试:
>>> import so02
>>> e = so02.example()
>>> e.text
''
>>> e.text = "foobar"
>>> e.text
'foobar'
注意:
set_example_text()
也许可以使用std::string
或bp::object
,这样我们就可以轻松获得长度,并且可能只允许从字符串中进行分配。答案 1 :(得分:1)
这扩展了Dan的答案。我写了一些生成lambda表达式的宏定义。这种方法的好处是它不依赖于特定类型或成员名称。
在我正在包装的API中,我有几百个要包装的类。这允许我为每个char*
类成员进行一次宏调用。
以下是Dan示例代码的修改版本:
#include <boost/python.hpp>
namespace bp = boost::python;
#define ADD_PROPERTY(TYPE, ATTR) add_property(#ATTR, SET_CHAR_P(TYPE, ATTR), \
GET_CHAR_P(TYPE, ATTR))
#define SET_CHAR_P(TYPE, ATTR) +[](const TYPE& e){ \
if (!e.ATTR) return ""; \
return (const char*)e.ATTR; \
}
#define GET_CHAR_P(TYPE, ATTR) +[](TYPE& e, char const* new_text){ \
delete[] e.ATTR; \
size_t n(strlen(new_text)); \
e.ATTR = new char[n+1]; \
strncpy(e.ATTR, new_text, n); \
e.ATTR[n] = '\0'; \
}
class example
{
public:
example()
{
text = new char[1];
text[0] = '\0';
}
~example()
{
delete[] text;
}
public:
char* text;
};
BOOST_PYTHON_MODULE(topics)
{
bp::class_<example>("example")
.ADD_PROPERTY(example, text);
}