使用pybind11将函数与std :: initializer_list参数绑定

时间:2018-02-27 01:04:12

标签: pybind11

我正在尝试使用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)

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

#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

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

2 个答案:

答案 0 :(得分:0)

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

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

有关详细信息,请参阅C++11 is it possible to construct an std::initializer_list?的答案。

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

答案 1 :(得分:0)

尽管作出了“无法支持”的论点,但使用cppyy(http://cppyy.org)却相当简单,但需要注意的是,您需要使用list_ints(1, 2, 3)来代替list_ints((1, 2, 3))。就是使用实际的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