From 51d18aa252332e883c61b7cb45ed317146b372ce Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 28 Apr 2017 11:06:16 -0400 Subject: [PATCH] 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({ 1, 2 }) which could be intepreted as either of: py::array_t(py::array_t(1, 2)) py::array_t(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. --- include/pybind11/buffer_info.h | 2 +- include/pybind11/common.h | 5 ----- include/pybind11/numpy.h | 17 ++++++++++------- tests/test_numpy_array.cpp | 6 ++++++ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h index e4029b983..e26e063af 100644 --- a/include/pybind11/buffer_info.h +++ b/include/pybind11/buffer_info.h @@ -36,7 +36,7 @@ struct buffer_info { } buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t size) - : buffer_info(ptr, itemsize, format, 1, size, itemsize) { } + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { } explicit buffer_info(Py_buffer *view, bool ownview = true) : buffer_info(view->buf, (size_t) view->itemsize, view->format, (size_t) view->ndim, diff --git a/include/pybind11/common.h b/include/pybind11/common.h index d9d9d5111..6df39f87b 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -690,11 +690,6 @@ public: template ::value>> any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } - // Implicit conversion constructor from any arithmetic type (only participates if T is also - // arithmetic). - template ::value && std::is_arithmetic::value>> - any_container(TIn singleton) : v(1, static_cast(singleton)) { } - // Avoid copying if given an rvalue vector of the correct type. any_container(std::vector &&v) : v(std::move(v)) { } diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index cbf4c2f96..44ea42721 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -458,7 +458,7 @@ public: forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ }; - array() : array(0, static_cast(nullptr)) {} + array() : array({{0}}, static_cast(nullptr)) {} using ShapeContainer = detail::any_container; using StridesContainer = detail::any_container; @@ -504,12 +504,9 @@ public: array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) : array(dt, std::move(shape), {}, ptr, base) { } - // This constructor is only needed to avoid ambiguity with the deprecated (handle, bool) - // constructor that comes from PYBIND11_OBJECT_CVT; once that is gone, the above constructor can - // handle it (because ShapeContainer is implicitly constructible from arithmetic types) - template ::value && !std::is_same::value>> - array(const pybind11::dtype &dt, T count) - : array(dt, count, nullptr) { } + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } template array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) @@ -519,6 +516,9 @@ public: array(ShapeContainer shape, const T *ptr, handle base = handle()) : array(std::move(shape), {}, ptr, base) { } + template + explicit array(size_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + explicit array(const buffer_info &info) : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } @@ -743,6 +743,9 @@ public: explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) : array(std::move(shape), ptr, base) { } + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + constexpr size_t itemsize() const { return sizeof(T); } diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 725254750..269f18bbe 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -267,4 +267,10 @@ test_initializer numpy_array([](py::module &m) { // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); + + // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous + sm.def("array_initializer_list", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2 }); }); + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3 }); }); + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3, 4 }); }); });