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?
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_cast
ing your function into a function that takes a different type. Pybind constructs astd::vector<int>
then passes this along as the function argument, but the function thinks it's getting astd::initializer_list
--and then Bad Things happen. It's basically doingauto &il = reintepret_cast<std::initializer_list<int> &>(v)
for the argument, wherev
is astd::vector<int>
.