Skip to content

Commit 51d18aa

Browse files
committed
Fix ambiguous initialize_list arguments
This removes the convert-from-arithemtic-scalar constructor of any_container as it can result in ambiguous calls, as in: py::array_t<float>({ 1, 2 }) which could be intepreted as either of: py::array_t<float>(py::array_t<float>(1, 2)) py::array_t<float>(py::detail::any_container({ 1, 2 })) Removing the convert-from-arithmetic constructor reduces the number of implicit conversions, avoiding the ambiguity for array and array_t. This also re-adds the array/array_t constructors taking a scalar argument for backwards compatibility.
1 parent 2761f78 commit 51d18aa

File tree

4 files changed

+17
-13
lines changed

4 files changed

+17
-13
lines changed

include/pybind11/buffer_info.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct buffer_info {
3636
}
3737

3838
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t size)
39-
: buffer_info(ptr, itemsize, format, 1, size, itemsize) { }
39+
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
4040

4141
explicit buffer_info(Py_buffer *view, bool ownview = true)
4242
: buffer_info(view->buf, (size_t) view->itemsize, view->format, (size_t) view->ndim,

include/pybind11/common.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,6 @@ class any_container {
690690
template <typename TIn, typename = enable_if_t<std::is_convertible<TIn, T>::value>>
691691
any_container(const std::initializer_list<TIn> &c) : any_container(c.begin(), c.end()) { }
692692

693-
// Implicit conversion constructor from any arithmetic type (only participates if T is also
694-
// arithmetic).
695-
template <typename TIn, typename = enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<TIn>::value>>
696-
any_container(TIn singleton) : v(1, static_cast<T>(singleton)) { }
697-
698693
// Avoid copying if given an rvalue vector of the correct type.
699694
any_container(std::vector<T> &&v) : v(std::move(v)) { }
700695

include/pybind11/numpy.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ class array : public buffer {
458458
forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
459459
};
460460

461-
array() : array(0, static_cast<const double *>(nullptr)) {}
461+
array() : array({{0}}, static_cast<const double *>(nullptr)) {}
462462

463463
using ShapeContainer = detail::any_container<Py_intptr_t>;
464464
using StridesContainer = detail::any_container<Py_intptr_t>;
@@ -504,12 +504,9 @@ class array : public buffer {
504504
array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle())
505505
: array(dt, std::move(shape), {}, ptr, base) { }
506506

507-
// This constructor is only needed to avoid ambiguity with the deprecated (handle, bool)
508-
// constructor that comes from PYBIND11_OBJECT_CVT; once that is gone, the above constructor can
509-
// handle it (because ShapeContainer is implicitly constructible from arithmetic types)
510-
template <typename T, typename = detail::enable_if_t<std::is_arithmetic<T>::value && !std::is_same<bool, T>::value>>
511-
array(const pybind11::dtype &dt, T count)
512-
: array(dt, count, nullptr) { }
507+
template <typename T, typename = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
508+
array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
509+
: array(dt, {{count}}, ptr, base) { }
513510

514511
template <typename T>
515512
array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
@@ -519,6 +516,9 @@ class array : public buffer {
519516
array(ShapeContainer shape, const T *ptr, handle base = handle())
520517
: array(std::move(shape), {}, ptr, base) { }
521518

519+
template <typename T>
520+
explicit array(size_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { }
521+
522522
explicit array(const buffer_info &info)
523523
: array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { }
524524

@@ -743,6 +743,9 @@ template <typename T, int ExtraFlags = array::forcecast> class array_t : public
743743
explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
744744
: array(std::move(shape), ptr, base) { }
745745

746+
explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle())
747+
: array({count}, {}, ptr, base) { }
748+
746749
constexpr size_t itemsize() const {
747750
return sizeof(T);
748751
}

tests/test_numpy_array.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,10 @@ test_initializer numpy_array([](py::module &m) {
267267
// Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object:
268268
sm.def("array_fail_test", []() { return py::array(py::object()); });
269269
sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
270+
271+
// Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
272+
sm.def("array_initializer_list", []() { return py::array_t<float>(1); }); // { 1 } also works, but clang warns about it
273+
sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2 }); });
274+
sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2, 3 }); });
275+
sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2, 3, 4 }); });
270276
});

0 commit comments

Comments
 (0)