Binding a function with std::initializer_list argu

2019-08-20 06:22发布

I am attempting to create python bindings using pybind11(v2.2.2+) and cannot figure out how to invoke a C function that has a single std::initializer_list argument.

void list_ints(std::initializer_list<int>)

And the pybind11 binding is:

m.def("list_ints", &list_ints)

From python, I'm trying to invoke like this:

list_ints(1, 2, 3)

Here is the sample C code compiled using llvm on MacOS with -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);
}

The python code contains a description of the results:

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)

This test code demonstrates that binding with arguments defined as const int &a, const int &b does match and invoke the list_ints function but something is clearly not right as a seg fault occurs upon accessing the arguments.

$ 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

Is there a way to bind and invoke void list_ints(std::initializer_list<int>) from Python?

标签: pybind11
1条回答
劫难
2楼-- · 2019-08-20 06:55

I received this answer from jagerman@github.com at the pybind11 repo:

It's not supported and, I believe, not possible to support: an initializer list is a deliberately opaque type designed to only be constructible by the C++ compiler but not by C++ code--and that means that it just isn't possible for us to accept it.

See the answer to C++11 is it possible to construct an std::initializer_list? for some more details.

As for your binding code, you're basically reintepret_casting your function into a function that takes a different type. Pybind constructs a std::vector<int> then passes this along as the function argument, but the function thinks it's getting a std::initializer_list--and then Bad Things happen. It's basically doing auto &il = reintepret_cast<std::initializer_list<int> &>(v) for the argument, where v is a std::vector<int>.

查看更多
登录 后发表回答