boost python 不会自动转换 char* 数据成员
boost python won't auto-convert char* data members
我正在尝试包装 C++ api,但我遇到了一些 char*
class 成员的障碍。似乎 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 我可以看到模板参数有这个签名:
template <class T, class Conversion, bool convertion_has_get_pytype_member=false>
因为 char*
不是 class 我猜这就是为什么它不起作用的原因。有人有一些见解吗?
更新2:
没有。结果 to_python_converter
需要在 BOOST_PYTHON_MODULE
调用中被调用。
我使 to_python_converter
正常工作(经过一些修改)。我还写了一个函数来转换形式python并注册到converter::registry::push_back
。我可以看到我的 to_python 代码 运行ning,但是 from_python 代码似乎从来没有 运行.
假设我们正在包装一些第三方 API,并抛开暴露这些指针并从外部对其进行处理的糟糕情况。
这是一个简短的概念证明:
#include <boost/python.hpp>
namespace bp = boost::python;
class example
{
public:
example()
{
text = new char[1];
text[0] = '[=10=]';
}
~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] = '[=10=]';
}
BOOST_PYTHON_MODULE(so02)
{
bp::class_<example>("example")
.add_property("text", &get_example_text, &set_example_text)
;
}
Class 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
以便我们可以轻松获得长度,并可能允许从不仅仅是字符串进行赋值。
- 如果要包装的成员变量很多,并且 getter/setter 模式相似,则使用模板生成代码,甚至只生成几个宏。
- 可能有一种方法可以用转换器做到这一点,明天我会研究一下。然而,当我们在这里处理内存管理时,我个人更喜欢这样处理,因为它会更明显地发生什么。
这扩展了 Dan 的回答。我写了一些生成 lambda 表达式的宏定义。这种方法的好处是它不依赖于特定类型或成员名称。
在我包装的 API 中,我有几百个 class 要包装。这允许我为每个 char*
class 成员调用一个宏。
这是 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] = '[=10=]'; \
}
class example
{
public:
example()
{
text = new char[1];
text[0] = '[=10=]';
}
~example()
{
delete[] text;
}
public:
char* text;
};
BOOST_PYTHON_MODULE(topics)
{
bp::class_<example>("example")
.ADD_PROPERTY(example, text);
}
我正在尝试包装 C++ api,但我遇到了一些 char*
class 成员的障碍。似乎 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 我可以看到模板参数有这个签名:
template <class T, class Conversion, bool convertion_has_get_pytype_member=false>
因为 char*
不是 class 我猜这就是为什么它不起作用的原因。有人有一些见解吗?
更新2:
没有。结果 to_python_converter
需要在 BOOST_PYTHON_MODULE
调用中被调用。
我使 to_python_converter
正常工作(经过一些修改)。我还写了一个函数来转换形式python并注册到converter::registry::push_back
。我可以看到我的 to_python 代码 运行ning,但是 from_python 代码似乎从来没有 运行.
假设我们正在包装一些第三方 API,并抛开暴露这些指针并从外部对其进行处理的糟糕情况。
这是一个简短的概念证明:
#include <boost/python.hpp>
namespace bp = boost::python;
class example
{
public:
example()
{
text = new char[1];
text[0] = '[=10=]';
}
~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] = '[=10=]';
}
BOOST_PYTHON_MODULE(so02)
{
bp::class_<example>("example")
.add_property("text", &get_example_text, &set_example_text)
;
}
Class 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
以便我们可以轻松获得长度,并可能允许从不仅仅是字符串进行赋值。- 如果要包装的成员变量很多,并且 getter/setter 模式相似,则使用模板生成代码,甚至只生成几个宏。
- 可能有一种方法可以用转换器做到这一点,明天我会研究一下。然而,当我们在这里处理内存管理时,我个人更喜欢这样处理,因为它会更明显地发生什么。
这扩展了 Dan 的回答。我写了一些生成 lambda 表达式的宏定义。这种方法的好处是它不依赖于特定类型或成员名称。
在我包装的 API 中,我有几百个 class 要包装。这允许我为每个 char*
class 成员调用一个宏。
这是 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] = '[=10=]'; \
}
class example
{
public:
example()
{
text = new char[1];
text[0] = '[=10=]';
}
~example()
{
delete[] text;
}
public:
char* text;
};
BOOST_PYTHON_MODULE(topics)
{
bp::class_<example>("example")
.ADD_PROPERTY(example, text);
}