Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] disambiguating lambda arguments in function overloads #3035

Open
rfeinman opened this issue Jun 13, 2021 · 3 comments · May be fixed by #5285
Open

[QUESTION] disambiguating lambda arguments in function overloads #3035

rfeinman opened this issue Jun 13, 2021 · 3 comments · May be fixed by #5285

Comments

@rfeinman
Copy link

rfeinman commented Jun 13, 2021

I have a question about writing overloaded function bindings and I could not find the answer anywhere in the docs or FAQ.

In my C++ code I have two overloaded variants of a function (call it "func"), each of which takes a single argument that is a std::function instance (lambda function). When I run my code in pure C++, disambiguation of the two argument types works just fine. However, when I try to write a pybind11 python module using the py::overload_cast tool, the disambiguation fails and I get a TypeError. Here is a simplified demonstration of the issue I'm running into. I am using C++14.

C++ source:

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>

namespace py = pybind11;

int func(const std::function<int(int)> &f) { return f(10); }

int func(const std::function<int(int,int)> &f) { return f(10,4); }

PYBIND11_MODULE(mymodule, m) {
    m.def("func", py::overload_cast<const std::function<int(int)> &>(&func));
    m.def("func", py::overload_cast<const std::function<int(int,int)> &>(&func));
}

Python source:

import mymodule

def f1(a):
    return 2*a

def f2(a, b):
    return 2*a + b

print(mymodule.func(f1))
print(mymodule.func(f2))

Python output:

20
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    print(mymodule.func(f2))
TypeError: f2() missing 1 required positional argument: 'b'

Is there a way to get the desired overloading behavior with pybind11? I've tried adding type hints to the f1 and f2 python functions, thinking perhaps this would help pybind11 disambiguate the different lambda arguments. No luck.

@illumitata
Copy link

Hi @rfeinman,

I found a similar question here: #1085

It looks like py::function is always being casted to first overload in resolution order. You can simply check that by switching the order of functions or using py::prepend(). Probably the only way to resolve your issue is to give each function a specific name.

Yet there is one solution I came up with (quite ugly and not easy to extend, since it's only based on number of parameters).

m.def("func",
        [](py::function &f)
        {
            py::print(py::detail::get_function(f));
            pybind11::module inspect_module = pybind11::module::import("inspect");
            pybind11::object result = inspect_module.attr("signature")(f).attr("parameters");
            auto num_params = pybind11::len(result);

            if (num_params == 1)
            {
                return func_cpp(py::cast<const std::function<int(int)> >(f));
            }
            else if (num_params == 2)
            {
                return func_cpp(py::cast<const std::function<int(int, int)> >(f));
            }
            // else {
            //     py::print("Error!");
            // }
        });

@rfeinman
Copy link
Author

Thank you @illumitata! This was super helpful

@aaronkossler
Copy link

aaronkossler commented Feb 6, 2022

I have a similar problem, where I try to use overload_cast with class member functions. My question is: How would you adjust @illumitata s solution for cpp class member functions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants