You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Python暴露含可选枚举列表的C++结构遇编译及运行异常

Boost Python枚举类与std::list/boost::optional互转问题解决

问题背景

C++定义

enum class Tag
{
    QR,
    April,
    Chili
};

enum class Constraint
{
    Tag,
    GPS,
    User
};

struct Options
{
   boost::optional<std::list<Tag>> tags;
   boost::optional<std::list<Constraint>> constraints;
};

编译错误

尝试自定义转换器时出现编译错误:

Build started...
1>------ Build started: Project: PyPackage, Configuration: Release x64 ------
1>Automatic MOC and UIC for target PyPackage
1>pypack.cpp
1>D:\dev\pypack.cpp(378,56): error C2678: binary '!': no operator found which takes a left-hand operand of type 'boost::python::extract<T>' (or there is no acceptable conversion)
1>        with
1>        [
1>            T=Tag
1>        ]
1>D:\dev\pypack.cpp(378,56): message : could be 'built-in C++ operator!(bool)'
1>D:\dev\pypack.cpp(378,56): message : while trying to match the argument list '(boost::python::extract<T>)'
1>        with
1>        [
1>            T=Tag
1>        ]
1>D:\dev\pypack.cpp(365,1): message : while compiling class template member function 'void *from_python_optional_list<Tag>::convertible(PyObject *)'
1>D:\dev\pypack.cpp(420,54): message : see reference to function template instantiation 'void *from_python_optional_list<Tag>::convertible(PyObject *)' being compiled
1>D:\dev\pypack.cpp(504,5): message : see reference to class template instantiation 'from_python_optional_list<Tag>' being compiled
1>D:\dev\pypack.cpp(378,21): error C2088: '!': illegal for struct
1>Done building project "PyPackage.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 42 up-to-date, 0 skipped ==========
========== Build started at 10:22 AM and took 09.334 seconds ==========

运行时错误

改用提取int再强转的方案后编译通过,但Python运行时出现类型不匹配:
Python代码:

import my_package
options = my_package.Options()
options.tags = [my_package.Tag.April]

异常信息:

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> >)

解决方法

1. 正确暴露枚举类到Python

使用BOOST_PYTHON_ENUM_CLASS注册枚举,让Boost Python能识别Python中的枚举实例:

#include <boost/python/enum.hpp>

namespace bp = boost::python;

// 注册Tag枚举
BOOST_PYTHON_ENUM_CLASS(Tag, Tag)
    .value("QR", Tag::QR)
    .value("April", Tag::April)
    .value("Chili", Tag::Chili);

// 注册Constraint枚举
BOOST_PYTHON_ENUM_CLASS(Constraint, Constraint)
    .value("Tag", Constraint::Tag)
    .value("GPS", Constraint::GPS)
    .value("User", Constraint::User);

2. 为std::list<枚举类型>编写转换器

实现从Python列表到C++std::list<Tag>的双向转换:

#include <boost/python/list.hpp>
#include <boost/python/converter.hpp>

struct ListTagConverter {
    // C++ list转Python列表
    static PyObject* convert(const std::list<Tag>& lst) {
        bp::list py_list;
        for (const auto& tag : lst) {
            py_list.append(bp::object(tag));
        }
        return bp::incref(py_list.ptr());
    }

    // 检查Python对象是否可转换为std::list<Tag>
    static void* convertible(PyObject* obj_ptr) {
        if (!PyList_Check(obj_ptr)) return nullptr;
        
        bp::list py_list(bp::handle<>(bp::borrowed(obj_ptr)));
        for (int i = 0; i < bp::len(py_list); ++i) {
            if (!bp::extract<Tag>(py_list[i]).check()) {
                return nullptr;
            }
        }
        return obj_ptr;
    }

    // 构造C++ std::list<Tag>对象
    static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data) {
        bp::list py_list(bp::handle<>(bp::borrowed(obj_ptr)));
        auto storage = reinterpret_cast<bp::converter::rvalue_from_python_storage<std::list<Tag>>*>(data)->storage.bytes;
        // 原地构造std::list
        new (storage) std::list<Tag>();
        auto lst = static_cast<std::list<Tag>*>(storage);

        for (int i = 0; i < bp::len(py_list); ++i) {
            lst->push_back(bp::extract<Tag>(py_list[i]));
        }
        data->convertible = storage;
    }
};

注册该转换器:

// 注册到Python的转换
bp::to_python_converter<std::list<Tag>, ListTagConverter>();
// 注册从Python到C++的转换
bp::converter::registry::push_back(
    &ListTagConverter::convertible,
    &ListTagConverter::construct,
    bp::type_id<std::list<Tag>>()
);

3. 为boost::optional<std::list<枚举>>编写转换器

处理可选列表的转换,支持Python的None值:

#include <boost/optional.hpp>

struct OptionalListTagConverter {
    // C++ optional list转Python对象(None或列表)
    static PyObject* convert(const boost::optional<std::list<Tag>>& opt_lst) {
        if (!opt_lst) {
            return bp::incref(Py_None);
        }
        return ListTagConverter::convert(*opt_lst);
    }

    // 检查Python对象是否可转换为optional<list<Tag>>
    static void* convertible(PyObject* obj_ptr) {
        if (obj_ptr == Py_None) return obj_ptr;
        return ListTagConverter::convertible(obj_ptr);
    }

    // 构造C++ optional<list<Tag>>对象
    static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data) {
        auto storage = reinterpret_cast<bp::converter::rvalue_from_python_storage<boost::optional<std::list<Tag>>>*>(data)->storage.bytes;
        
        if (obj_ptr == Py_None) {
            // 构造空的optional
            new (storage) boost::optional<std::list<Tag>>();
            data->convertible = storage;
            return;
        }

        // 先构造std::list<Tag>,再包装到optional中
        std::list<Tag> lst;
        bp::converter::rvalue_from_python_stage1_data sub_data{};
        sub_data.convertible = &lst;
        ListTagConverter::construct(obj_ptr, &sub_data);

        new (storage) boost::optional<std::list<Tag>>(std::move(lst));
        data->convertible = storage;
    }
};

注册该转换器:

bp::to_python_converter<boost::optional<std::list<Tag>>, OptionalListTagConverter>();
bp::converter::registry::push_back(
    &OptionalListTagConverter::convertible,
    &OptionalListTagConverter::construct,
    bp::type_id<boost::optional<std::list<Tag>>>()
);

4. 暴露Options结构体

最后将Options暴露给Python:

bp::class_<Options>("Options")
    .def_readwrite("tags", &Options::tags)
    .def_readwrite("constraints", &Options::constraints);

关键注意事项

  • 编译错误原因:boost::python::extract<T>没有重载!运算符,需使用.check()方法检查是否可提取目标类型。
  • 运行时错误原因:未为std::list<Tag>注册转换器,Boost Python无法自动完成Python列表到C++枚举列表的转换。
  • 对Constraint类型的处理可复用上述模板,只需将Tag替换为Constraint即可。

内容的提问来源于stack exchange,提问作者Cyril

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:50:57