pybind / pybind/pybind11

pybind11 will downcast is stead of using overload resolution resulting in unexpected behavior

Open
#1,783 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
18k
Forks
2.3k
Avg merge
5d 17h
Merged PRs (30d)
10

Description

Coming from C++ I expected overload resolution to be maintained through pybind11. This is not the case. I dont know if it is not possible to implement in pybind11. If that is the case, It could be (more) clearly stated in the documentation, and a feature request would be that the overload function priority could be explicitly given.

An example:

#include <pybind11/pybind11.h>

namespace py = pybind11;

class A
{
public:
    A(int v_) : v{ v_ } {}
    int val() const { return v; }
    void val(int i) { v = i; }
private:
    int v = 0;
};

class B: public A
{
public:
    B(int v) : A{ v-1 } {}
    int val() const { return A::val() + 1; }
    void val(int i) { A::val(i-1); }
};

template <typename T>
T& f(T& t, int i) { t.val(i); return t; }

PYBIND11_MODULE(pyreftest, m) {
    py::class_<A>(m, "A")
        .def(py::init<int>())
        .def("__repr__", [](const A&a) {return "<A(" + std::to_string(a.val()) + ")>";  });
    py::class_<B,A>(m, "B")
        .def(py::init<int>())
        .def("__repr__", [](const B&b) {return "<B(" + std::to_string(b.val()) + ")>";  });
    m.def("f", &f<A>);
    m.def("f", &f<B>);
}

Result:

In [1]: from pyreftest import *

In [2]: b = B(3)

In [3]: b
Out[3]: <B(3)>

In [4]: f(b,4)
Out[4]: <A(4)>

In [5]: b
Out[5]: <B(5)>

Expected result:

In [1]: from pyreftest import *

In [2]: b = B(3)

In [3]: b
Out[3]: <B(3)>

In [4]: f(b,4)
Out[4]: <B(4)>

In [5]: b
Out[5]: <B(4)>

I realize that a workaround is to swap the two last lines of code i.e:

    m.def("f", &f<B>);
    m.def("f", &f<A>);

but I think this is more a workaround than a fix.

Otherwise awesome project
Best regards Troels

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied pyreftest reproducer and the two m.def("f", ...) registrations, then inspect how overload resolution handles A and B arguments. Done would be a confirmed behavior with either an implementation and regression test or clear documentation of the limitation and the registration-order workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
api, backend-api-design
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.