diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3a13ce4e..af64045c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.7, 3.8, 3.9] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true miniconda-version: "latest" @@ -26,6 +26,9 @@ jobs: environment-file: etc/conda-forge-testing.yaml activate-environment: ndarray + - name: Print conda environment + run: conda list -n ndarray + - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands diff --git a/CHANGELOG.md b/CHANGELOG.md index e350d59f..33a8de5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # ndarray change log +## 1.6.5 + +### Bug fixes + +Fix compilation issues with newer compiler and boost versions. + ## 1.5.1 ### Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index cc61976b..477666c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,3 +32,6 @@ if(NDARRAY_TEST) add_subdirectory(tests) endif(NDARRAY_TEST) +# installation +install(DIRECTORY include/ DESTINATION include/ + FILES_MATCHING PATTERN "*.h") diff --git a/etc/conda-forge-testing.yaml b/etc/conda-forge-testing.yaml index ce4fc26e..2a4f1a3d 100644 --- a/etc/conda-forge-testing.yaml +++ b/etc/conda-forge-testing.yaml @@ -7,5 +7,7 @@ dependencies: - fftw - numpy - pybind11 - - gxx_linux-64 + - c-compiler - eigen + - cmake + - pkg-config diff --git a/include/ndarray/ArrayBase.h b/include/ndarray/ArrayBase.h index 79325028..085a44c0 100644 --- a/include/ndarray/ArrayBase.h +++ b/include/ndarray/ArrayBase.h @@ -85,11 +85,7 @@ class ArrayBase : public ExpressionBase { /// @brief Return a single element from the array. Element & operator[](Index const & i) const { - return *(this->_data + this->_core-> - #ifndef _MSC_VER - template - #endif - computeOffset(i)); + return *(this->_data + this->_core->computeOffset(i)); } /// @brief Return an Iterator to the beginning of the array. diff --git a/include/ndarray/Vector.h b/include/ndarray/Vector.h index f8e476c6..13e6cb07 100644 --- a/include/ndarray/Vector.h +++ b/include/ndarray/Vector.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/include/ndarray/formatting.h b/include/ndarray/formatting.h index e3f34b87..d716033e 100644 --- a/include/ndarray/formatting.h +++ b/include/ndarray/formatting.h @@ -19,6 +19,7 @@ #include "ndarray/ExpressionBase.h" +#include #include namespace ndarray { diff --git a/include/ndarray/operators.h b/include/ndarray/operators.h index 9d671444..5ff06cbe 100644 --- a/include/ndarray/operators.h +++ b/include/ndarray/operators.h @@ -18,7 +18,6 @@ #include "ndarray/Array.h" #include -#include #include #include "ndarray/detail/UnaryOp.h" @@ -64,6 +63,64 @@ struct BinaryPredicate { typedef bool result_type; }; +// Local helper classes to avoid using the Boost ones, +// which inherit from boost::unary_function and boost::binary_function +// Boost might use the std implementations underneath, +// which are removed in the C++17 standard, but are still supported by some compilers +// in C++20 + +template +struct binary_traits +{ + typedef Operation function_type; + typedef const function_type & param_type; + typedef typename Operation::result_type result_type; + typedef typename Operation::first_argument_type first_argument_type; + typedef typename Operation::second_argument_type second_argument_type; +}; + +template +class _binder1st { +public: + using result_type = typename binary_traits::result_type; + + _binder1st(typename binary_traits::param_type x, + typename boost::call_traits::first_argument_type>::param_type y) + : + op(x), value(y) {} + + typename binary_traits::result_type + operator()( + typename boost::call_traits::second_argument_type>::param_type x) const { + return op(value, x); + } + +protected: + typename binary_traits::function_type op; + typename binary_traits::first_argument_type value; +}; + +template +class _binder2nd { +public: + using result_type = typename binary_traits::result_type; + + _binder2nd(typename binary_traits::param_type x, + typename boost::call_traits::second_argument_type>::param_type y) + : + op(x), value(y) {} + + typename binary_traits::result_type + operator()( + typename boost::call_traits::first_argument_type>::param_type x) const { + return op(x, value); + } + +protected: + typename binary_traits::function_type op; + typename binary_traits::second_argument_type value; +}; + /** * \internal @class AdaptableFunctionTag * \brief A CRTP base class for non-template classes that contain a templated functor. @@ -78,7 +135,7 @@ struct AdaptableFunctionTag { typedef typename Derived::template ScalarFunction< A, typename ExpressionTraits::Element > BinaryFunction; - typedef boost::binder1st Bound; + typedef _binder1st Bound; static Bound bind(A const & scalar) { return Bound(BinaryFunction(),scalar); } @@ -89,7 +146,7 @@ struct AdaptableFunctionTag { typedef typename Derived::template ScalarFunction< typename ExpressionTraits::Element, B > BinaryFunction; - typedef boost::binder2nd Bound; + typedef _binder2nd Bound; static Bound bind(B const & scalar) { return Bound(BinaryFunction(),scalar); } diff --git a/include/ndarray/pybind11.h b/include/ndarray/pybind11.h index 5d216dee..bd1eaeda 100644 --- a/include/ndarray/pybind11.h +++ b/include/ndarray/pybind11.h @@ -120,6 +120,13 @@ Pybind11Helper { pybind11_np_size_t const * strides = wrapper.strides(); pybind11_np_size_t const itemsize = wrapper.itemsize(); if (C > 0) { + // If the shape is zero in any dimension, we don't + // worry about the strides. + for (int i = 0; i < C; ++i) { + if (shape[N-i-1] == 0) { + return true; + } + } pybind11_np_size_t requiredStride = itemsize; for (int i = 0; i < C; ++i) { if (strides[N-i-1] != requiredStride) { @@ -128,6 +135,13 @@ Pybind11Helper { requiredStride *= shape[N-i-1]; } } else if (C < 0) { + // If the shape is zero in any dimension, we don't + // worry about the strides. + for (int i = 0; i < -C; ++i) { + if (shape[i] == 0) { + return true; + } + } pybind11_np_size_t requiredStride = itemsize; for (int i = 0; i < -C; ++i) { if (strides[i] != requiredStride) { diff --git a/tests/pybind11_test.py b/tests/pybind11_test.py index 1c913ed6..d6b2f719 100644 --- a/tests/pybind11_test.py +++ b/tests/pybind11_test.py @@ -38,16 +38,19 @@ def testStrideHandling(self): # in NumPy 1.8+ 1- and 0-sized arrays can have arbitrary strides; we should # be able to handle those array = numpy.zeros(1, dtype=float) - # just test that these don't throw - pybind11_test_mod.acceptArray10(array) - pybind11_test_mod.acceptArray10(array) + # the zero shape array tests are simply checking that pybind11 can handle + # arbitrary strides (non-zero for length 1 array, zero for length 0 array + # for numpy >= 1.23). + pybind11_test_mod.acceptAnyArray10(array) + pybind11_test_mod.acceptAnyArray11(array) array = numpy.zeros(0, dtype=float) - pybind11_test_mod.acceptArray10(array) - pybind11_test_mod.acceptArray10(array) + pybind11_test_mod.acceptAnyArray10(array) + pybind11_test_mod.acceptAnyArray11(array) # test that we gracefully fail when the strides are no multiples of the itemsize dtype = numpy.dtype([("f1", numpy.float64), ("f2", numpy.int16)]) table = numpy.zeros(3, dtype=dtype) - self.assertRaises(TypeError, pybind11_test_mod.acceptArray10, table['f1']) + self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray10, table['f1']) + self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray11, table['f1']) def testNone(self): array = numpy.zeros(10, dtype=float) @@ -60,7 +63,8 @@ def testNonNativeByteOrder(self): d2 = numpy.dtype(">f8") nonnative = d2 if d1 == numpy.dtype(float) else d1 a = numpy.zeros(5, dtype=nonnative) - self.assertRaises(TypeError, pybind11_test_mod.acceptArray10, a) + self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray10, a) + self.assertRaises(TypeError, pybind11_test_mod.acceptAnyArray11, a) if __name__ == "__main__": diff --git a/tests/pybind11_test_mod.cc b/tests/pybind11_test_mod.cc index dae388aa..464eb2cb 100644 --- a/tests/pybind11_test_mod.cc +++ b/tests/pybind11_test_mod.cc @@ -39,7 +39,9 @@ bool acceptArray1(ndarray::Array const & a1) { #endif } -void acceptArray10(ndarray::Array const & a1) {} +void acceptAnyArray10(ndarray::Array const & a1) {} + +void acceptAnyArray11(ndarray::Array const & a1) {} bool acceptArray3(ndarray::Array const & a1) { ndarray::Array a2 = returnArray3(); @@ -69,7 +71,8 @@ PYBIND11_MODULE(pybind11_test_mod, mod) { mod.def("returnArray3", returnArray3); mod.def("returnConstArray3", returnConstArray3); mod.def("acceptArray1", acceptArray1); - mod.def("acceptArray10", acceptArray10); + mod.def("acceptAnyArray10", acceptAnyArray10); + mod.def("acceptAnyArray11", acceptAnyArray11); mod.def("acceptArray3", acceptArray3); mod.def("acceptNoneArray", acceptNoneArray, "array"_a = nullptr); -} \ No newline at end of file +}