Open sizmailov opened 4 years ago
Python/Pybind11 doesn't know that Base
is a base class of Derived
. You can make this work like so:
#include "pybind11/pybind11.h"
#include "pybind11/embed.h"
namespace py = pybind11;
struct Base {
int size() const noexcept { return 0; }
};
struct Derived : Base {};
PYBIND11_EMBEDDED_MODULE(example, m) {
py::class_<Base>(m, ""); // Bind the base class
py::class_<Derived, Base>(m, "Derived") // Tell python about the inheritance hierarchy
.def(py::init<>())
.def("size", &Derived::size);
}
int main() {
py::scoped_interpreter guard{};
py::exec(R"(
from example import Derived
d = Derived()
print(d.size())
)");
}
Sorry for unclear description. In my example I don't want to expose Base
class to python. The snippet works fine without noexcept
specifier, so I still think it's a bug.
We recently added a special case for ref-qualified functions (https://github.com/pybind/pybind11/commit/63df87fa490d49244b76249854559ec8db22f119), now this seems like yet another annotation that would need to be supported. Not super-happy about the potential combinatorial explosion here, it would be nice if C++ would let us template over such things..
it would be nice if C++ would let us template over such things..
Did someone say Reflections? ...
I think it might actually be possible to template over such things (at least in C++17 which is the only place it is necessary) by abusing/reimplementing std::invoke: https://stackoverflow.com/questions/60852108/overload-regardless-noexcept-specification (C++11 backport available here): https://web.archive.org/web/20150811205403/https://github.com/tomaszkam/proposals/blob/master/invoke/invoke_cpp11.hpp Stumped on how to actually implement this though, we may need to use std::invoke_result
Okay, there is definitely a way around templating this, but I am bit lost on how to refactor this to proceed. We just need to use std::mem_fn and std::invoke (backported) to acomplish this. The C+11 backport could look something like this:
#if defined(PYBIND11_CPP17)
using std::invoke;
#else
template<typename Functor, typename Object, typename... Args>
constexpr auto invoke(Functor&& functor, Object&& object, Args&&... args)
-> typename std::enable_if<
std::is_member_function_pointer<
typename std::decay<Functor>::type
>::value &&
type_traits::is_target_reference<
Object&&,
typename std::decay<Functor>::type
>::value,
decltype((object.*functor)(std::forward<Args>(args)...))
>::type
{
return (object.*functor)(std::forward<Args>(args)...);
}
template<typename Functor, typename Object, typename... Args>
constexpr auto invoke(Functor&& functor, Object&& object, Args&&... args)
-> typename std::enable_if<
std::is_member_function_pointer<
typename std::decay<Functor>::type
>::value &&
!type_traits::is_target_reference<
Object&&,
typename std::decay<Functor>::type
>::value,
decltype(((*std::forward<Object>(object)).*functor)(std::forward<Args>(args)...))
>::type
{
return ((*std::forward<Object>(object)).*functor)(std::forward<Args>(args)...);
}
template<typename Functor, typename Object>
constexpr auto invoke(Functor&& functor, Object&& object)
-> typename std::enable_if<
std::is_member_object_pointer<
typename std::decay<Functor>::type
>::value &&
type_traits::is_target_reference<
Object&&,
typename std::decay<Functor>::type
>::value,
decltype(object.*functor)
>::type
{
return object.*functor;
}
template<typename Functor, typename Object>
constexpr auto invoke(Functor&& functor, Object&& object)
-> typename std::enable_if<
std::is_member_object_pointer<
typename std::decay<Functor>::type
>::value &&
!type_traits::is_target_reference<
Object&&,
typename std::decay<Functor>::type
>::value,
decltype((*std::forward<Object>(object)).*functor)
>::type
{
return (*std::forward<Object>(object)).*functor;
}
template<typename Functor, typename... Args>
constexpr auto invoke(Functor&& functor, Args&&... args)
-> typename std::enable_if<
!std::is_member_pointer<
typename std::decay<Functor>::type
>::value,
decltype(std::forward<Functor>(functor)(std::forward<Args>(args)...))
>::type
{
return std::forward<Functor>(functor)(std::forward<Args>(args)...);
}
#endif
This template is horrific, but may be the easiest way to use downstream as it just converts noexcept functions types to none-noexcept function types. https://stackoverflow.com/a/55701361/2444240 Would still need to refactor the initialize overloads to use std::mem_fn though which seems difficult.
Places that need updating are listed in this comment: https://github.com/pybind/pybind11/issues/2856#issuecomment-785020501
I'm running into this problem. I have a base class, which uses noexcept and which I cannot change, that pybind11 fails to find. Is there a workaround that I can use in the derived class to get around this?
Issue description
Base class methods attributed with
noexcept
are not recognized by pybind11 overload system in C++17 mode. The example below compiles just fine in C++11/C++14 mode. Probably it's related to:https://en.cppreference.com/w/cpp/language/noexcept_spec#Explanation
Reproducible example code
Output (C++17):
(tried with g++-8 / clang-9)