wjakob / nanobind

nanobind: tiny and efficient C++/Python bindings
BSD 3-Clause "New" or "Revised" License
2.27k stars 185 forks source link

[BUG]: Incorrect use of templated function used when including uint32_t input #365

Closed mscroggs closed 10 months ago

mscroggs commented 10 months ago

Problem description

When wrapping a templated function with a ndarray<T> and uint32_t inputs, it's wrongly using the wrapped version with T=double when I pass in a complex-valued array.

Minimal failing code is in this repo and included below

Reproducible example code

#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/stl/complex.h>
#include <span>

namespace nb = nanobind;

namespace {

template <typename T> void swap(std::span<T> data, std::uint32_t index) {
  std::swap(data[0], data[1]);
};
}

NB_MODULE(nanobind_example_ext, m) {
  m.def("swap", [](nb::ndarray<double, nb::ndim<1>, nb::c_contig> data,
                   std::uint32_t index) {
    swap(std::span(data.data(), data.size()), index);
  });
  m.def("swap",
        [](nb::ndarray<std::complex<double>, nb::ndim<1>, nb::c_contig> data,
           std::uint32_t index) {
          swap(std::span(data.data(), data.size()), index);
        });
}

This code leads to the following test failing when (and only when) index is np.uint32(1):

@pytest.mark.parametrize("index", [1, np.uint32(1)])
def test_uint32_complex(index):
    data = np.array([1.0 + 2.0j, 3.0 + 4.0j])
    m.swap(data, index)
    assert np.allclose(data, [3.0 + 4.0j, 1.0 + 2.0j])
wjakob commented 10 months ago

Any relation to the just-opened issue #364?

mscroggs commented 10 months ago

Any relation to the just-opened issue #364?

Yes, we both found these while debugging issues in FEniCSx. Thought it worth still opening this in case the smaller example I found is useful. If not, feel free to close this

wjakob commented 10 months ago

Fixed, thanks for the report.