使用Boost Python暴露含std::list枚举成员的结构体时赋值失败的问题求助
使用Boost Python暴露含std::list枚举成员的结构体时赋值失败的问题求助
嘿,我一看你这个报错就知道问题出在哪了——你写的from_python_list_enum转换器是把Python列表里的元素当成原生int来处理的,但实际上Python里的my_package.Tag.April是Boost Python包装后的枚举对象,不是普通的int,所以boost::python::extract<int>会直接失败,导致转换器认为这个列表不符合要求,最终抛出类型不匹配的错误。
核心问题分析
你之前处理int列表没问题,是因为Python原生int可以直接被extract<int>提取,但Boost包装的枚举对象不一样,它是一个特殊的Python对象,得用对应的枚举类型E来提取才行。
解决方案
我们只需要修改from_python_list_enum里的convertible和construct函数,让它们能正确识别并处理Boost包装的枚举对象:
1. 修改convertible函数,检查元素是否为目标枚举类型
把原来检查int的逻辑改成检查枚举类型E:
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) { PyObject* item = PyList_GetItem(obj_ptr, i); // 直接检查是否能提取为枚举类型E if (!boost::python::extract<E>(item).check()) return nullptr; } return obj_ptr; }
2. 修改construct函数,直接提取枚举类型
不需要先转成int再强转,直接提取枚举值:
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 = reinterpret_cast<std::list<E>*>(storage); size_t sz = PySequence_Size(obj_ptr); for (size_t i = 0; i < sz; ++i) { PyObject* item = PyList_GetItem(obj_ptr, i); // 直接提取枚举类型E的值 E v = boost::python::extract<E>(item); l->push_back(v); } }
进阶:同时支持枚举对象和int值
如果你想让用户既可以传[my_package.Tag.April, my_package.Tag.QR],也可以传对应的int值(比如[1, 0]),可以把转换器改成兼容两种情况:
修改后的convertible函数:
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) { PyObject* item = PyList_GetItem(obj_ptr, i); // 同时检查是否是枚举对象或者int bool is_valid_enum = boost::python::extract<E>(item).check(); bool is_valid_int = boost::python::extract<int>(item).check(); if (!is_valid_enum && !is_valid_int) return nullptr; } return obj_ptr; }
对应的construct函数:
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 = reinterpret_cast<std::list<E>*>(storage); size_t sz = PySequence_Size(obj_ptr); for (size_t i = 0; i < sz; ++i) { PyObject* item = PyList_GetItem(obj_ptr, i); E v; // 优先提取枚举类型,不行再转int if (boost::python::extract<E>(item).check()) { v = boost::python::extract<E>(item); } else { int int_val = boost::python::extract<int>(item); v = static_cast<E>(int_val); } l->push_back(v); } }
验证修改
把上面的代码替换掉原来的转换器实现,重新编译你的包,然后再运行Python代码:
import my_package options = my_package.Options() options.tags = [my_package.Tag.April, my_package.Tag.QR] # 试试传int值也能生效:options.tags = [1, 0]
这次应该就能正常赋值,不会再抛出类型不匹配的错误了。
备注:内容来源于stack exchange,提问作者Cyril
相关产品推荐
相关产品推荐

