Python function returning an instance of STL container cannot be wrapped in `std::function`
First of all, thanks for the great project!
I'm having an issue passing python function as std::function<std::vector<int>(std::vector<int>)> For example:
import cppyy
import cppyy.gbl as cpp
cppyy.cppdef(r"""\
int apply_transform(int value, const std::function<int(int)>& transform) { return transform(value); }
"""
)
def add(value : 'int') -> 'int':
return value + 1
print("transformed 1 is {}".format(cpp.apply_transform(1, add_one)))
The code above succeeds with printing transformed 1 is 2.
However, if I modify function types to be STL container, e.g. std::vector, as follows:
import cppyy
import cppyy.gbl as cpp
cppyy.cppdef(r"""\
std::vector<int> apply_transform(std::vector<int> value, const std::function<std::vector<int>(std::vector<int>)>& transform) { return transform(value); }
"""
)
vector_int = cpp.std.vector[int]
def add_one(value : vector_int) -> vector_int:
return vector_int([v+1 for v in value])
value = vector_int([2, 3])
print("transformed {} is {}".format(value, cpp.apply_transform(value, add_one)))
then I get an error:
Traceback (most recent call last):
File "/home/yershov/SynologyDrive/code/emp/examples/function_test.py", line 15, in <module>
print("transformed {} is {}".format(value, cpp.apply_transform(value, add_one)))
NotImplementedError: std::vector<int> ::apply_transform(std::vector<int> value, const std::function<std::vector<int>(std::vector<int,std::allocator<int> >)>& transform) =>
NotImplementedError: could not convert argument 2 (this method cannot (yet) be called)
I experimented with std::array and other STL containers to no avail.
Currently, I'm on cppyy==2.4.1
For some reason the typedef resolution when applied to std::function<std::vector<int>(std::vector<int>)> produces complete gobbledygook. Then, the resulting type can no longer be decomposed it the parts necessary to wrap up the Python callable and the result is an argument conversion failure. :/
Thanks @wlav . Does that mean, it's not possible (yet)?
@wlav I found a solution. Clunky, but works:
import cppyy
import cppyy.gbl as cpp
cppyy.cppdef(r"""\
template<typename R, typename... Args>
struct Function {
virtual ~Function() = default;
virtual R run(Args... args) const = 0;
};
std::vector<int> apply_transform(std::vector<int> value, const Function<std::vector<int>, std::vector<int>>& transform) { return transform.run(value); }
"""
)
vector_int = cpp.std.vector[int]
class AddOne(cpp.Function[vector_int, vector_int]):
def run(self, value: vector_int) -> vector_int:
return vector_int([v+1 for v in value])
add_one = AddOne()
value = vector_int([2, 3])
print("transformed {} is {}".format(value, cpp.apply_transform(value, add_one)))
Yes, I was thinking along similar lines. The following exemplifies a similar workaround that can be removed once cppyy is fixed:
import cppyy
import cppyy.gbl as cpp
cppyy.cppdef(r"""\
std::vector<int> apply_transform(std::vector<int> value, const std::function<std::vector<int>(std::vector<int>)>& transform) {
return transform(value);
}
template<class T>
T apply_transform_workaround(T value, T(*transform)(T)) {
return apply_transform(value, transform);
}
""")
# temp workaround --
cppyy.gbl.apply_transform = cppyy.gbl.apply_transform_workaround
# -- temp workaround
# further code unaffected ...
vector_int = cpp.std.vector[int]
def add_one(value : vector_int) -> vector_int:
return vector_int([v+1 for v in value])
value = vector_int([2, 3])
print("transformed {} is {}".format(value, cpp.apply_transform(value, add_one)))
That's great. I didn't know that raw function pointers are not susceptible to this problem. Thanks!