pybind / pybind/pybind11

[BUG]: Specifying return type `Eigen::Map<...>` in `PYBIND11_OVERRIDE_PURE` gives compilation error

Open
#5,622 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Required prerequisites
What version (or hash if on master) of pybind11 are you using?

2.11.1-2 (installed on ubuntu 24.04 via apt)

Problem description

According to the comment at https://github.com/pybind/pybind11/blob/bc4a66dff0464d0c87291b00a3688999fea5bedc/include/pybind11/eigen/matrix.h#L451-L453 pybind11 should support returning of Eigen::Map types from class methods, and it does work. However when I want to return a Map from a purely virtual function using PYBIND11_OVERRIDE_PURE it gives an error

Reproducible example code
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl.h>

#include <Eigen/Eigen>

// typedef Eigen::MatrixXd T;
typedef Eigen::Map<Eigen::MatrixXd> T;

class A {
    virtual T f() = 0;
};


class PyA : A {
    virtual T f() override {
        PYBIND11_OVERRIDE_PURE(T, A, f);
    }
};

gives:

In file included from /usr/include/pybind11/attr.h:14,
                 from /usr/include/pybind11/detail/class.h:12,
                 from /usr/include/pybind11/pybind11.h:13,
                 from /home/sin3point14/random/cpp/pybind11-test/main.cpp:1:
/usr/include/pybind11/cast.h: In instantiation of ‘typename pybind11::detail::make_caster<T>::cast_op_type<typename std::add_rvalue_reference<_Tp>::type> pybind11::detail::cast_op(make_caster<T>&&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename make_caster<T>::cast_op_type<typename std::add_rvalue_reference<_Tp>::type> = Eigen::Map<Eigen::Matrix<double, -1, -1> >; make_caster<T> = type_caster<Eigen::Map<Eigen::Matrix<double, -1, -1> >, void>; typename std::add_rvalue_reference<_Tp>::type = Eigen::Map<Eigen::Matrix<double, -1, -1> >&&]’:
/usr/include/pybind11/cast.h:1053:22:   required from ‘T pybind11::cast(const handle&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename std::enable_if<((! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && (! std::is_same<typename detail::remove_cvref<T>::type, _object*>::value)), int>::type <anonymous> = 0]’
/usr/include/pybind11/cast.h:1159:19:   required from ‘std::enable_if_t<((! std::is_base_of<pybind11::detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && pybind11::detail::negation<std::integral_constant<bool, (pybind11::detail::move_always<T>::value || pybind11::detail::move_if_unreferenced<T>::value)> >::value), T> pybind11::cast(object&&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; std::enable_if_t<((! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && detail::negation<std::integral_constant<bool, (detail::move_always<T>::value || detail::move_if_unreferenced<T>::value)> >::value), T> = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename std::remove_reference<_Tp>::type = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
/usr/include/pybind11/cast.h:1226:29:   required from ‘std::enable_if_t<pybind11::detail::negation<std::integral_constant<bool, (std::integral_constant<bool, (((std::is_reference< <template-parameter-1-1> >::value || std::is_pointer<_Tp>::value) && (! std::is_base_of<pybind11::detail::type_caster_generic, pybind11::detail::type_caster<typename pybind11::detail::intrinsic_type<T>::type> >::value)) && (! std::is_same<typename pybind11::detail::intrinsic_type<T>::type, void>::value))>::value || std::is_void<_Tp>::value)> >::value, T> pybind11::detail::cast_safe(pybind11::object&&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; std::enable_if_t<negation<std::integral_constant<bool, (std::integral_constant<bool, (((std::is_reference< <template-parameter-1-1> >::value || std::is_pointer<_Tp>::value) && (! std::is_base_of<type_caster_generic, type_caster<typename intrinsic_type<T>::type> >::value)) && (! std::is_same<typename intrinsic_type<T>::type, void>::value))>::value || std::is_void<_Tp>::value)> >::value, T> = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename intrinsic_type<T>::type = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
/home/sin3point14/random/cpp/pybind11-test/main.cpp:17:9:   required from here
/usr/include/pybind11/cast.h:51:75: error: use of deleted function ‘pybind11::detail::eigen_map_caster<MapType>::operator MapType() [with MapType = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
   50 |     return std::move(caster).operator typename make_caster<T>::
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~            
   51 |         template cast_op_type<typename std::add_rvalue_reference<T>::type>();
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
In file included from /usr/include/pybind11/eigen.h:12,
                 from /home/sin3point14/random/cpp/pybind11-test/main.cpp:2:
/usr/include/pybind11/eigen/matrix.h:446:5: note: declared here
  446 |     operator MapType() = delete;
      |     ^~~~~~~~
/usr/include/pybind11/cast.h: In instantiation of ‘pybind11::detail::type_caster<T, SFINAE>& pybind11::detail::load_type(type_caster<T, SFINAE>&, const pybind11::handle&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; SFINAE = void]’:
/usr/include/pybind11/cast.h:1037:14:   required from ‘pybind11::detail::make_caster<T> pybind11::detail::load_type(const pybind11::handle&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; make_caster<T> = type_caster<Eigen::Map<Eigen::Matrix<double, -1, -1> >, void>]’
/usr/include/pybind11/cast.h:1053:35:   required from ‘T pybind11::cast(const handle&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename std::enable_if<((! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && (! std::is_same<typename detail::remove_cvref<T>::type, _object*>::value)), int>::type <anonymous> = 0]’
/usr/include/pybind11/cast.h:1159:19:   required from ‘std::enable_if_t<((! std::is_base_of<pybind11::detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && pybind11::detail::negation<std::integral_constant<bool, (pybind11::detail::move_always<T>::value || pybind11::detail::move_if_unreferenced<T>::value)> >::value), T> pybind11::cast(object&&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; std::enable_if_t<((! std::is_base_of<detail::pyobject_tag, typename std::remove_reference<_Tp>::type>::value) && detail::negation<std::integral_constant<bool, (detail::move_always<T>::value || detail::move_if_unreferenced<T>::value)> >::value), T> = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename std::remove_reference<_Tp>::type = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
/usr/include/pybind11/cast.h:1226:29:   required from ‘std::enable_if_t<pybind11::detail::negation<std::integral_constant<bool, (std::integral_constant<bool, (((std::is_reference< <template-parameter-1-1> >::value || std::is_pointer<_Tp>::value) && (! std::is_base_of<pybind11::detail::type_caster_generic, pybind11::detail::type_caster<typename pybind11::detail::intrinsic_type<T>::type> >::value)) && (! std::is_same<typename pybind11::detail::intrinsic_type<T>::type, void>::value))>::value || std::is_void<_Tp>::value)> >::value, T> pybind11::detail::cast_safe(pybind11::object&&) [with T = Eigen::Map<Eigen::Matrix<double, -1, -1> >; std::enable_if_t<negation<std::integral_constant<bool, (std::integral_constant<bool, (((std::is_reference< <template-parameter-1-1> >::value || std::is_pointer<_Tp>::value) && (! std::is_base_of<type_caster_generic, type_caster<typename intrinsic_type<T>::type> >::value)) && (! std::is_same<typename intrinsic_type<T>::type, void>::value))>::value || std::is_void<_Tp>::value)> >::value, T> = Eigen::Map<Eigen::Matrix<double, -1, -1> >; typename intrinsic_type<T>::type = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
/home/sin3point14/random/cpp/pybind11-test/main.cpp:17:9:   required from here
/usr/include/pybind11/cast.h:1018:19: error: use of deleted function ‘bool pybind11::detail::eigen_map_caster<MapType>::load(pybind11::handle, bool) [with MapType = Eigen::Map<Eigen::Matrix<double, -1, -1> >]’
 1018 |     if (!conv.load(handle, true)) {
      |          ~~~~~~~~~^~~~~~~~~~~~~~
/usr/include/pybind11/eigen/matrix.h:445:10: note: declared here
  445 |     bool load(handle, bool) = delete;
      |          ^~~~
gmake[2]: *** [CMakeFiles/tsting.dir/build.make:76: CMakeFiles/tsting.dir/main.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/tsting.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

If I don't use Map:

diff --git a/main.cpp b/main.cpp
index 266892a..e700e1d 100644
--- a/main.cpp
+++ b/main.cpp
@@ -4,8 +4,8 @@
 
 #include <Eigen/Eigen>
 
-// typedef Eigen::MatrixXd T;
-typedef Eigen::Map<Eigen::MatrixXd> T;
+typedef Eigen::MatrixXd T;
+// typedef Eigen::Map<Eigen::MatrixXd> T;
 
 class A {
     virtual T f() = 0;

it compiles fine

Is this a regression? Put the last known working version here if it is.

Not a regression

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

Reproduce the failure from the posted main.cpp using pybind11/eigen.h and Eigen/Eigen, then inspect include/pybind11/eigen/matrix.h around lines 445-453 and the PYBIND11_OVERRIDE_PURE path. Done means the Eigen::Map return type compiles for the pure virtual override without breaking existing Eigen caster behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.