[BUG]: implicitly_convertible with templated constructor
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 18k
- Forks
- 2.3k
- Avg merge
- 5d 17h
- Merged PRs (30d)
- 10
Description
Required prerequisites
- Make sure you've read the documentation. Your issue may be addressed there.
- Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
2.13.1
Problem description
Could we make implicitly_convertible explicitly instantiate templated constructors?
Consider this type erasure pattern (remark the template constructor of A for implicit conversion).
#pragma once
#include <memory>
#include <iostream>
// Concept and Model classes for type erasure
namespace detail {
struct Concept {
virtual ~Concept() = default;
virtual void print() const = 0;
virtual std::unique_ptr<Concept> clone() const = 0;
};
template <typename T>
struct Model : Concept {
T data;
Model(T const& data) : data(data) {}
void print() const override { data.print(); }
std::unique_ptr<Concept> clone() const override { return std::make_unique<Model<T>>(*this); }
};
}
// Type-Erased Class A
class A {
std::unique_ptr<detail::Concept> pimpl;
public:
template <typename T>
A(T const& x) : pimpl(std::make_unique<detail::Model<T>>(x)) {}
A(A const& other) : pimpl(other.pimpl->clone()) {}
A& operator=(A const& other) { pimpl = other.pimpl->clone(); return *this; }
A(A&& other) noexcept = default;
A& operator=(A&& other) noexcept = default;
void print() const { pimpl->print(); }
};
// Specialized Class B
class B {
public:
void print() const {
std::cout << "I am B" << std::endl;
}
};
// Specialized Class C
class C {
public:
void print() const {
std::cout << "I am C" << std::endl;
}
};
void print(const A& foo) {
foo.print();
}
The python binding:
PYBIND11_MODULE(example, m) {
py::class_<A>(m, "A")
.def("print", &A::print);
py::class_<B>(m, "B")
.def(py::init<>())
.def("print", &B::print);
py::class_<C>(m, "C")
.def(py::init<>())
.def("print", &C::print);
py::implicitly_convertible<B, A>();
py::implicitly_convertible<C, A>();
m.def("print", &print);
}
I would expect the python binding to behave like:
import example
b = example.B()
print_from_b = example.print(b) # This should print "I am B" due to implicit conversion
c = example.C()
print_from_c = example.print(c) # This should print "I am C" due to implicit conversion
The error traceback:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: printA(): incompatible function arguments. The following argument types are supported:
1. (arg0: example.A) -> None
Invoked with: <example.B object at 0x75f99e0c3130>
Reproducible example code
No response
Is this a regression? Put the last known working version here if it is.
Not a regression
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the py::implicitly_convertible<B, A>() and py::implicitly_convertible<C, A>() entry points used in the binding example, then reproduce the failure with the shown A, B, and C types. Done means calls to m.def("print", &print) accept both example.B and example.C and preserve their respective print behavior, with coverage for templated constructors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100