Boost Python:公开 std::enums 列表

问题描述 投票:0回答:1

我有以下结构:

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::可选)。

c++ boost boost-python
1个回答
0
投票

您的

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]
    
© www.soinside.com 2019 - 2024. All rights reserved.