我有以下结构:
enum class Tag
{
QR,
April,
Chili
}
struct Options
{
std::list<Tag> tags;
}
我想使用 boost python 在 Python 包中公开。
这是我将列表与 python 相互转换的代码:
// Convert std list to python list
template<typename T>
struct std_list_to_python
{
static PyObject* convert(std::list<T> const& l)
{
boost::python::list result;
for (auto const& value : l)
{
result.append(value);
}
return boost::python::incref(result.ptr());
}
};
// Convert Python list of enum to std::list of enum
template<typename E>
struct from_python_list_enum
{
static void* convertible(PyObject* obj_ptr)
{
if (!PyList_Check(obj_ptr)) {
return nullptr;
}
size_t sz = PySequence_Size(obj_ptr);
for (size_t i = 0; i < sz; ++i)
{
if (!boost::python::extract<int>::extract(PyList_GetItem(obj_ptr, i)))
return nullptr;
}
return obj_ptr;
}
static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data)
{
typedef boost::python::converter::rvalue_from_python_storage<std::list<E>> storage_type;
void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
data->convertible = new (storage) std::list<E>();
std::list<E>* l = (std::list<E>*)(storage);
size_t sz = PySequence_Size(obj_ptr);
for (size_t i = 0; i < sz; ++i)
{
int v = typename boost::python::extract<int>::extract(PyList_GetItem(obj_ptr, i));
l->push_back(static_cast<E>(v));
}
}
from_python_list_enum()
{
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<std::list<E>>());
}
};
这是导出类的代码:
void export_test()
{
enum_<Tag>("Tag", "Values for Tag")
.value("QR", Tag::QR)
.value("April", Tag::April)
.value("Chili", Tag::Chili)
;
boost::python::to_python_converter<std::list<Tag>, std_list_to_python<Tag>>();
from_python_list_enum<Tag>();
class_<Options>("Options", "Struct with a list of enum")
.add_property("tags", make_getter(&Options::tags, return_value_policy<return_by_value>()), make_setter(&Options::tags, return_value_policy<return_by_value>()), "Tags")
;
}
编译工作正常。但是,在运行以下 Python 代码时:
import my_package
options = my_package.Options()
options.tags = [my_package.Tag.April, my_package.Tag.QR]
我在最后一行有以下异常:
Boost.Python.ArgumentError
Python argument types in
None.None(Options, list)
did not match C++ signature:
None(struct Options{lvalue}, class std::list<enum Tag,class std::allocator<enum Tag> >)
我该怎么做才能让这项工作成功?
注意:我可以使用非常相似的代码来包装整数列表,而不会出现问题。我还测试了从 int 到 enum 的转换在另一种情况下是否有效(boost::可选)。
您的
convertible
支票有错误:
if (!BP::extract<int>(PyList_GetItem(obj_ptr, i)))
return nullptr;
不检查是否可以提取int,而是检查返回的整数值是true
- 当上下文转换为bool时。您可以通过从列表中省略
QR
值来见证这一点,例如
print([my_package.Tag.April, my_package.Tag.QR])
options.tags = [my_package.Tag.April, my_package.Tag.Chili]
print(options.tags)
print("\nNow the trouble starts:\n--")
options.tags = [my_package.Tag.April, my_package.Tag.QR]
给出以下输出:
[my_package.Tag.April, my_package.Tag.QR]
[my_package.Tag.April, my_package.Tag.Chili]
Now the trouble starts:
--
Traceback (most recent call last):
File "/home/sehe/Projects/stackoverflow/./test.py", line 12, in <module>
options.tags = [my_package.Tag.April, my_package.Tag.QR]
Boost.Python.ArgumentError: Python argument types in
None.None(Options, list)
did not match C++ signature:
None(Options {lvalue}, std::__cxx11::list<Tag, std::allocator<Tag> >)
修复它,例如通过做
BP::extract<int> n(PyList_GetItem(obj_ptr, i));
if (!n.check())
return nullptr;
现在输出是
[my_package.Tag.April, my_package.Tag.QR]
[my_package.Tag.April, my_package.Tag.Chili]
Now the trouble starts:
--
[my_package.Tag.April, my_package.Tag.QR]