1

我正在尝试使用 pybind11(v2.2.2+) 创建 python 绑定,但无法弄清楚如何调用具有单个 std::initializer_list 参数的 C 函数。

void list_ints(std::initializer_list<int>)

pybind11 绑定是:

m.def("list_ints", &list_ints)

从python,我试图这样调用:

list_ints(1, 2, 3)

这是在 MacOS 上使用 llvm 编译的示例 C 代码-std=C++14

#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

using namespace std;
namespace py = pybind11;

void list_ints(const std::initializer_list<int> &il) {
    std::cout << "Got to list_ints ..." << std::endl;
    for (const auto &elem : il)
        std::cout << to_string(elem) << " ";
    std::cout << std::endl;
};

PYBIND11_MODULE(initializer, m) {
    m.def("list_ints", &list_ints);
    m.def("list_ints", (void (*) (const std::initializer_list<int>&)) &list_ints);
    # This is the only binding that seems to work .. sort of.
    m.def("list_ints", (void (*) (const int &a, const int &b)) &list_ints);
}

python 代码包含对结果的描述:

from initializer import list_ints

try:
    # Fails with: TypeError: Incompatible function arguments
    print("Calling list_ints(1, 2, 3)")
    list_ints(1, 2, 3)
except TypeError as err:
    print(err)

# Call succeeds but function Seg Faults!
print("Calling list_ints(1, 2)")
list_ints(1,2)

此测试代码演示了与定义为的参数的绑定const int &a, const int &b确实匹配并调用了 list_ints 函数,但显然有些事情是不正确的,因为在访问参数时会发生 seg 错误。

$ python initializer.py
Calling list_ints(1, 2, 3)
list_ints(): incompatible function arguments. The following argument types are supported:
    1. (arg0: std::initializer_list<int>) -> None
    2. (arg0: std::initializer_list<int>) -> None
    3. (arg0: int, arg1: int) -> None

Invoked with: 1, 2, 3

Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.
Calling list_ints(1, 2)
Got to list_ints ...
Segmentation fault: 11

有没有办法void list_ints(std::initializer_list<int>)从 Python 绑定和调用?

4

2 回答 2

1

尽管提出了“不可能支持”的论点,但实际上使用 cppyy ( http://cppyy.org ) 相当简单,但需要注意的是,list_ints(1, 2, 3)您需要使用list_ints((1, 2, 3)). IE。使用实际的 python 集合,而不是 3 个参数(比较如何初始化 numpy 数组;同样的事情):

import cppyy

cppyy.cppdef(r"""void list_ints(std::initializer_list<int> ll) {
    for (auto i: ll)
        std::cerr << i << '\n';
}""")

cppyy.gbl.list_ints((1, 2, 3))

打印预期的:

1
2
3
于 2019-11-17T21:53:26.153 回答
0

我从 pybind11 repo 的 jagerman@github.com 收到了这个答案:

它不受支持,我相信也不可能支持:初始化列表是一种故意不透明的类型,设计为只能由 C++ 编译器而不是 C++ 代码构造——这意味着我们不可能接受。

查看 C++11 的答案是否可以构造 std::initializer_list?了解更多详情。

至于您的绑定代码,您基本上是reintepret_cast将您的函数转换为一个采用不同类型的函数。Pybind 构造 astd::vector<int>然后将其作为函数参数传递,但函数认为它得到了一个std::initializer_list-- 然后坏事发生了。它基本上是auto &il = reintepret_cast<std::initializer_list<int> &>(v)为了争论,哪里vstd::vector<int>.

于 2018-02-28T17:20:50.900 回答