├── numpy_boost ├── __init__.py └── test.py ├── MANIFEST.in ├── setup.py ├── src ├── _numpy_boost_test.cpp └── _numpy_boost_python_test.cpp ├── README.rst └── include ├── numpy_boost_python.hpp └── numpy_boost.hpp /numpy_boost/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README 2 | include include/*.hpp 3 | include src/*.cpp 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | import numpy 4 | try: 5 | numpy_include = numpy.get_include() 6 | except AttributeError: 7 | numpy_include = numpy.get_numpy_include() 8 | 9 | print(""" 10 | NOTE: This setup.py is for testing and demonstration purposes only. 11 | To use numpy-boost as a library, simply put numpy-boost.hpp on your 12 | C++ include path. 13 | """) 14 | 15 | TEST_BOOST_PYTHON = True 16 | 17 | extensions = [] 18 | 19 | extensions.append( 20 | Extension('numpy_boost._numpy_boost_test', 21 | ['src/_numpy_boost_test.cpp'], 22 | include_dirs=['include', numpy_include], 23 | undef_macros=['NDEBUG'] 24 | ) 25 | ) 26 | 27 | if TEST_BOOST_PYTHON: 28 | extensions.append( 29 | Extension('numpy_boost._numpy_boost_python_test', 30 | ['src/_numpy_boost_python_test.cpp'], 31 | include_dirs=['include', numpy_include], 32 | undef_macros=['NDEBUG'], 33 | libraries=['boost_python'] 34 | ) 35 | ) 36 | 37 | setup( 38 | name="numpy_boost", 39 | version="0.1", 40 | description = "C++ wrappers for Numpy", 41 | packages=['numpy_boost'], 42 | ext_modules=extensions) 43 | -------------------------------------------------------------------------------- /numpy_boost/test.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def test(): 4 | from numpy_boost import _numpy_boost_test 5 | 6 | x = numpy.zeros((3, 4, 6), order="C") 7 | print x.strides 8 | for i in range(x.shape[0]): 9 | for j in range(x.shape[1]): 10 | for k in range(x.shape[2]): 11 | x[i][j][k] = (i+1)*(j+1)*(k+1) 12 | 13 | # Do a funny striding on the array 14 | strided = x[::2,1:2,::3] 15 | 16 | print "Python" 17 | for i in range(strided.shape[0]): 18 | print '[', 19 | for j in range(strided.shape[1]): 20 | print '[', 21 | for k in range(strided.shape[2]): 22 | print strided[i][j][k], 23 | print ']', 24 | print ']', 25 | 26 | print 27 | 28 | result = _numpy_boost_test.test(strided) 29 | 30 | print "result" 31 | print result 32 | 33 | try: 34 | result = _numpy_boost_test.test(None) 35 | except ValueError: 36 | pass 37 | else: 38 | assert False, "Exception not thrown" 39 | 40 | def test_boost_python(): 41 | try: 42 | from numpy_boost import _numpy_boost_python_test 43 | except ImportError: 44 | return 45 | 46 | x = numpy.zeros((3, 4, 6), order="C") 47 | print x.strides 48 | for i in range(x.shape[0]): 49 | for j in range(x.shape[1]): 50 | for k in range(x.shape[2]): 51 | x[i][j][k] = (i+1)*(j+1)*(k+1) 52 | 53 | # Do a funny striding on the array 54 | strided = x[::2,1:2,::3] 55 | 56 | result = _numpy_boost_python_test.test(strided) 57 | 58 | print "result" 59 | print result 60 | 61 | if __name__ == "__main__": 62 | test() 63 | test_boost_python() 64 | -------------------------------------------------------------------------------- /src/_numpy_boost_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "numpy_boost.hpp" 3 | 4 | extern "C" PyObject* 5 | test(PyObject* self, PyObject* args, PyObject* kwds) { 6 | try { 7 | // Convert the incoming array to a numpy_boost object. 8 | // It must be convertable to DOUBLE, 3 dimensional or an 9 | // exception will be thrown. 10 | numpy_boost array(args); 11 | 12 | // The array, even if strided in a funny way, should not 13 | // have been copied. 14 | assert(args == array.py_ptr()); 15 | 16 | // Write out the array contents using [] syntax 17 | std::cout << "C++" << std::endl; 18 | for (size_t i = 0; i < array.shape()[0]; ++i) { 19 | std::cout << "["; 20 | for (size_t j = 0; j < array.shape()[1]; ++j) { 21 | std::cout << "["; 22 | for (size_t k = 0; k < array.shape()[2]; ++k) { 23 | std::cout << array[i][j][k] << " "; 24 | } 25 | std::cout << "]"; 26 | } 27 | std::cout << "]"; 28 | } 29 | std::cout << std::endl; 30 | 31 | // Create a new numpy_boost array (with a new Numpy array to back 32 | // its memory), it fill it with some contents. 33 | int dims[] = { 32, 64 }; 34 | numpy_boost array2(dims); 35 | 36 | for (size_t i = 0; i < array2.shape()[0]; ++i) { 37 | for (size_t j = 0; j < array2.shape()[1]; ++j) { 38 | array2[i][j] = i * j; 39 | } 40 | } 41 | 42 | // Return the Numpy array back to Python. 43 | PyObject* array2_obj = array2.py_ptr(); 44 | Py_INCREF(array2_obj); 45 | return array2_obj; 46 | } catch (const python_exception& e) { 47 | return NULL; 48 | } 49 | } 50 | 51 | static PyMethodDef module_methods[] = { 52 | {"test", (PyCFunction)test, METH_O, ""}, 53 | {NULL} 54 | }; 55 | 56 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 57 | #define PyMODINIT_FUNC void 58 | #endif 59 | extern "C" 60 | PyMODINIT_FUNC 61 | init_numpy_boost_test(void) { 62 | PyObject* m; 63 | 64 | m = Py_InitModule3("_numpy_boost_test", module_methods, ""); 65 | 66 | if (m == NULL) 67 | return; 68 | 69 | import_array(); 70 | } 71 | -------------------------------------------------------------------------------- /src/_numpy_boost_python_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "numpy_boost_python.hpp" 3 | 4 | /* 5 | This demonstrates how to use numpy_boost with boost.python. 6 | 7 | */ 8 | 9 | using namespace boost::python; 10 | 11 | numpy_boost test(const numpy_boost& array) 12 | { 13 | // Write out the array contents using [] syntax 14 | std::cout << "C++" << std::endl; 15 | for (size_t i = 0; i < array.shape()[0]; ++i) { 16 | std::cout << "["; 17 | for (size_t j = 0; j < array.shape()[1]; ++j) { 18 | std::cout << "["; 19 | for (size_t k = 0; k < array.shape()[2]; ++k) { 20 | std::cout << array[i][j][k] << " "; 21 | } 22 | std::cout << "]"; 23 | } 24 | std::cout << "]"; 25 | } 26 | std::cout << std::endl; 27 | 28 | // Create a new numpy_boost array (with a new Numpy array to back 29 | // its memory), it fill it with some contents. 30 | int dims[] = { 32, 64 }; 31 | numpy_boost array2(dims); 32 | 33 | for (size_t i = 0; i < array2.shape()[0]; ++i) { 34 | for (size_t j = 0; j < array2.shape()[1]; ++j) { 35 | array2[i][j] = i * j; 36 | } 37 | } 38 | 39 | return array2; 40 | } 41 | 42 | 43 | class ClassWithArray { 44 | public: 45 | numpy_boost m_x; 46 | 47 | ClassWithArray(numpy_boost array) : 48 | m_x(array) 49 | { 50 | 51 | } 52 | 53 | numpy_boost 54 | get_array() 55 | { 56 | return m_x; 57 | } 58 | 59 | void 60 | set_array(numpy_boost array) 61 | { 62 | m_x = array; 63 | } 64 | }; 65 | 66 | 67 | BOOST_PYTHON_MODULE(_numpy_boost_python_test) 68 | { 69 | /* Initialize the Numpy support */ 70 | import_array(); 71 | 72 | /* You must call this function inside of the BOOST_PYTHON_MODULE 73 | init function to set up the type conversions. It must be called 74 | for every type and dimensionality of array you intend to return. 75 | 76 | If you don't, the code will compile, but you will get error 77 | messages at runtime. 78 | */ 79 | numpy_boost_python_register_type(); 80 | numpy_boost_python_register_type(); 81 | 82 | /* Declare a function that takes and returns an array */ 83 | def("test", test); 84 | 85 | /* Declare a class with a public array member that we want to 86 | expose as a Python property */ 87 | class_("ClassWithArray", init >()) 88 | 89 | /* It's a shame that adding a property to a custom class is so 90 | convoluted in Boost.Python, but so it is... See FAQ "Why is 91 | my automatic to-python conversion not being found?" */ 92 | .add_property( 93 | "x", 94 | make_getter(&ClassWithArray::m_x, return_value_policy()), 95 | make_setter(&ClassWithArray::m_x, return_value_policy())) 96 | 97 | /* I think it's somewhat less "magical" to just do this */ 98 | .add_property( 99 | "y", &ClassWithArray::get_array, &ClassWithArray::set_array); 100 | 101 | } 102 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | This code provides a very thin wrapper around `Numpy 2 | `__ arrays to make them accessible in C++ as 3 | `boost::multi_array 4 | `__ 5 | objects. Wherever possible, the data itself is not copied, even when 6 | the data is noncontiguous. 7 | 8 | Advantages over using the raw Numpy/C API are: 9 | 10 | - Cleaner syntax without using macros, e.g. indexing, iterators 11 | 12 | - Automatic integration with Python reference counting, for easier 13 | memory management 14 | 15 | - Any C++ algorithm written to the ``boost::multi_array`` interface 16 | can easily be recompiled to work with Numpy arrays. 17 | 18 | Known shortcomings: 19 | 20 | - Due to the design of boost::multi_array, the datatype and number of 21 | dimensions of the array is fixed at compile time (though this is 22 | often not an important limitation in practice). 23 | 24 | - Some features in Numpy arrays are not supported by 25 | ``boost::multi_array``, and therefore require an implicit data copy on 26 | conversion. These include: 27 | 28 | - Values in non-native endianess 29 | 30 | - Object arrays and recarrays are not supported. 31 | 32 | .. note:: 33 | 34 | This code is currently experimental and lacks adequate 35 | documentation and unit tests. 36 | 37 | Prerequisites 38 | ------------- 39 | 40 | - Boost 1.34 or later 41 | 42 | - Numpy 1.1 or later 43 | 44 | - Python 2.3 or later 45 | 46 | Compilation 47 | ----------- 48 | 49 | The ``setup.py`` script included in the source tree is for testing and 50 | demonstration purposes only. You do not need to build or install it 51 | to start using this library. 52 | 53 | The entirety of this project exists in a two header files 54 | ``include/numpy-boost.hpp``, which handles the bridge between Numpy 55 | and ``boost::multi_array``, and the optional 56 | ``include/numpy-boost-python.hpp`` which is helpful if you are 57 | wrapping your library with ``boost::python``. These need to be on 58 | your C++ include path, as well as Numpy's headers. Since everything 59 | is implemented in header files, you do not need to link to any 60 | additional libraries. 61 | 62 | If using distutils to build your Python extension module, this can be 63 | achieved with the following:: 64 | 65 | from distutils.core import setup, Extension 66 | 67 | import numpy 68 | try: 69 | numpy_include = numpy.get_include() 70 | except AttributeError: 71 | numpy_include = numpy.get_numpy_include() 72 | 73 | setup(name="my_project", 74 | version="0.1", 75 | description = "My project that uses numpy-boost", 76 | packages=['my_project'], 77 | ext_modules=[ 78 | Extension('my_project.extension_module', 79 | ['src/my_project_source.cpp'], 80 | include_dirs=[PATH_TO_NUMPY_BOOST_HPP, numpy_include] 81 | ) 82 | ] 83 | ) 84 | 85 | Beyond that, your extension module should do all of the things 86 | required of a Numpy-using extension module. See the Numpy 87 | documentation for more information. 88 | 89 | Usage 90 | ----- 91 | 92 | To create a ``numpy_boost`` array based on an existing Numpy array:: 93 | 94 | PyArrayObject* numpy_array; // passed into function 95 | 96 | ... 97 | 98 | numpy_boost array(numpy_array); 99 | 100 | where the template arguments to numpy_boost are the data type and the 101 | number of dimensions. 102 | 103 | To create a new empty ``numpy_boost`` array:: 104 | 105 | int dims[] = { 32, 64 }; 106 | numpy_boost array(dims); 107 | 108 | To return a Numpy array back to the caller, use the ``py_ptr()`` 109 | method. Note this returns a borrowed reference:: 110 | 111 | PyObject* result = array.py_ptr(); 112 | Py_INCREF(result); 113 | return result; 114 | -------------------------------------------------------------------------------- /include/numpy_boost_python.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Michael Droettboom 3 | All rights reserved. 4 | 5 | Licensed under the BSD license. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the following 16 | disclaimer in the documentation and/or other materials provided 17 | with the distribution. 18 | 19 | * The names of its contributors may not be used to endorse or 20 | promote products derived from this software without specific 21 | prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | /* 37 | This header defines type conversions for use with Boost.Python. If 38 | not using numpy_boost with Boost.Python, do not include this header. 39 | 40 | Each numpy_boost type that you wish to pass or return from a function 41 | must be declared in the init function by calling 42 | numpy_boost_python_register_type. 43 | 44 | For example, if your module returns numpy_boost types, the 45 | module's init function must call: 46 | 47 | numpy_boost_python_register_type; 48 | */ 49 | 50 | #ifndef __NUMPY_BOOST_PYTHON_HPP__ 51 | #define __NUMPY_BOOST_PYTHON_HPP__ 52 | 53 | #include "numpy_boost.hpp" 54 | #include 55 | #include 56 | 57 | template 58 | struct numpy_boost_to_python 59 | { 60 | static PyObject* 61 | convert(const numpy_boost& o) 62 | { 63 | return boost::python::incref(o.py_ptr()); 64 | } 65 | }; 66 | 67 | template 68 | struct numpy_boost_from_python 69 | { 70 | static void* 71 | convertible(PyObject* obj_ptr) 72 | { 73 | PyArrayObject* a; 74 | 75 | a = (PyArrayObject*)PyArray_FromObject( 76 | obj_ptr, ::detail::numpy_type_map(), NDims, NDims); 77 | if (a == NULL) { 78 | return 0; 79 | } 80 | Py_DECREF(a); 81 | return obj_ptr; 82 | } 83 | 84 | static void 85 | construct( 86 | PyObject* obj_ptr, 87 | boost::python::converter::rvalue_from_python_stage1_data* data) 88 | { 89 | // Grab pointer to memory into which to construct the new numpy_boost object 90 | void* storage = ( 91 | (boost::python::converter::rvalue_from_python_storage >*) 92 | data)->storage.bytes; 93 | 94 | // in-place construct the new numpy_boost object using the character data 95 | // extraced from the python object 96 | new (storage) numpy_boost(obj_ptr); 97 | 98 | // Stash the memory chunk pointer for later use by boost.python 99 | data->convertible = storage; 100 | } 101 | 102 | numpy_boost_from_python() 103 | { 104 | boost::python::converter::registry::push_back( 105 | &convertible, 106 | &construct, 107 | boost::python::type_id >()); 108 | } 109 | }; 110 | 111 | 112 | template 113 | void 114 | numpy_boost_python_register_type() 115 | { 116 | boost::python::to_python_converter< 117 | numpy_boost, 118 | numpy_boost_to_python >(); 119 | 120 | numpy_boost_from_python converter; 121 | } 122 | 123 | #endif /* __NUMPY_BOOST_PYTHON_HPP__ */ 124 | -------------------------------------------------------------------------------- /include/numpy_boost.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Michael Droettboom 3 | All rights reserved. 4 | 5 | Licensed under the BSD license. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the following 16 | disclaimer in the documentation and/or other materials provided 17 | with the distribution. 18 | 19 | * The names of its contributors may not be used to endorse or 20 | promote products derived from this software without specific 21 | prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #ifndef __NUMPY_BOOST_HPP__ 37 | #define __NUMPY_BOOST_HPP__ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | /* numpy_type_map 48 | 49 | Provides a mapping from C++ datatypes to Numpy type 50 | numbers. */ 51 | namespace detail { 52 | template 53 | int numpy_type_map() { 54 | throw std::runtime_error("numpy_type_map(): Illegal conversion requested"); 55 | } 56 | 57 | // Must be inlined to avoid multiple definitions since they are fully 58 | // specialized function templates. 59 | template<> inline int numpy_type_map() { return NPY_FLOAT; } 60 | template<> inline int numpy_type_map >() { return NPY_CFLOAT; } 61 | template<> inline int numpy_type_map() { return NPY_DOUBLE; } 62 | template<> inline int numpy_type_map >() { return NPY_CDOUBLE; } 63 | template<> inline int numpy_type_map() { return NPY_LONGDOUBLE; } 64 | template<> inline int numpy_type_map >() { return NPY_CLONGDOUBLE; } 65 | template<> inline int numpy_type_map() { return NPY_INT8; } 66 | template<> inline int numpy_type_map() { return NPY_UINT8; } 67 | template<> inline int numpy_type_map() { return NPY_INT16; } 68 | template<> inline int numpy_type_map() { return NPY_UINT16; } 69 | template<> inline int numpy_type_map() { return NPY_INT32; } 70 | template<> inline int numpy_type_map() { return NPY_UINT32; } 71 | template<> inline int numpy_type_map() { return NPY_INT64; } 72 | template<> inline int numpy_type_map() { return NPY_UINT64; } 73 | } 74 | 75 | class python_exception : public std::exception { 76 | 77 | }; 78 | 79 | /* An array that acts like a boost::multi_array, but is backed by the 80 | memory of a Numpy array. Provides nice C++ interface to a Numpy 81 | array without any copying of the data. 82 | 83 | It may be constructed one of two ways: 84 | 85 | 1) With an existing Numpy array. The boost::multi_array will 86 | then have the data, dimensions and strides of the Numpy array. 87 | 88 | 2) With a list of dimensions, in which case a new contiguous 89 | Numpy array will be created and the new boost::array will 90 | point to it. 91 | 92 | */ 93 | template 94 | class numpy_boost : public boost::multi_array_ref 95 | { 96 | public: 97 | typedef numpy_boost self_type; 98 | typedef boost::multi_array_ref super; 99 | typedef typename super::size_type size_type; 100 | typedef T* TPtr; 101 | 102 | private: 103 | PyArrayObject* array; 104 | 105 | void init_from_array(PyArrayObject* a) throw() { 106 | /* Upon calling init_from_array, a should already have been 107 | incref'd for ownership by this object. */ 108 | 109 | /* Store a reference to the Numpy array so we can DECREF it in the 110 | destructor. */ 111 | array = a; 112 | 113 | /* Point the boost::array at the Numpy array data. 114 | 115 | We don't need to worry about free'ing this pointer, because it 116 | will always point to memory allocated as part of the data of a 117 | Numpy array. That memory is managed by Python reference 118 | counting. */ 119 | super::base_ = (TPtr)PyArray_DATA(a); 120 | 121 | /* Set the storage order. 122 | 123 | It would seem like we would want to choose C or Fortran 124 | ordering here based on the flags in the Numpy array. However, 125 | those flags are purely informational, the actually information 126 | about storage order is recorded in the strides. */ 127 | super::storage_ = boost::c_storage_order(); 128 | 129 | /* Copy the dimensions from the Numpy array to the boost::array. */ 130 | boost::detail::multi_array::copy_n(PyArray_DIMS(a), NDims, super::extent_list_.begin()); 131 | 132 | /* Copy the strides from the Numpy array to the boost::array. 133 | 134 | Numpy strides are in bytes. boost::array strides are in 135 | elements, so we need to divide. */ 136 | for (size_t i = 0; i < NDims; ++i) { 137 | super::stride_list_[i] = PyArray_STRIDE(a, i) / sizeof(T); 138 | } 139 | 140 | /* index_base_list_ stores the bases of the indices in each 141 | dimension. Since we want C-style and Numpy-style zero-based 142 | indexing, just fill it with zeros. */ 143 | std::fill_n(super::index_base_list_.begin(), NDims, 0); 144 | 145 | /* We don't want any additional offsets. If they exist, Numpy has 146 | already handled that for us when calculating the data pointer 147 | and strides. */ 148 | super::origin_offset_ = 0; 149 | super::directional_offset_ = 0; 150 | 151 | /* Calculate the number of elements. This has nothing to do with 152 | memory layout. */ 153 | super::num_elements_ = std::accumulate(super::extent_list_.begin(), 154 | super::extent_list_.end(), 155 | size_type(1), 156 | std::multiplies()); 157 | } 158 | 159 | public: 160 | /* Construct from an existing Numpy array */ 161 | numpy_boost(PyObject* obj) : 162 | super(NULL, std::vector(NDims, 0)), 163 | array(NULL) 164 | { 165 | PyArrayObject* a; 166 | 167 | a = (PyArrayObject*)PyArray_FromObject( 168 | obj, ::detail::numpy_type_map(), NDims, NDims); 169 | if (a == NULL) { 170 | throw boost::python::error_already_set(); 171 | } 172 | 173 | init_from_array(a); 174 | } 175 | 176 | /* Copy constructor */ 177 | numpy_boost(const self_type &other) throw() : 178 | super(NULL, std::vector(NDims, 0)), 179 | array(NULL) 180 | { 181 | Py_INCREF(other.array); 182 | init_from_array(other.array); 183 | } 184 | 185 | /* Construct a new array based on the given dimensions */ 186 | template 187 | explicit numpy_boost(const ExtentsList& extents) : 188 | super(NULL, std::vector(NDims, 0)), 189 | array(NULL) 190 | { 191 | npy_intp shape[NDims]; 192 | PyArrayObject* a; 193 | 194 | boost::detail::multi_array::copy_n(extents, NDims, shape); 195 | 196 | a = (PyArrayObject*)PyArray_SimpleNew( 197 | NDims, shape, ::detail::numpy_type_map()); 198 | if (a == NULL) { 199 | throw boost::python::error_already_set(); 200 | } 201 | 202 | init_from_array(a); 203 | } 204 | 205 | /* Destructor */ 206 | ~numpy_boost() { 207 | /* Dereference the numpy array. */ 208 | Py_XDECREF(array); 209 | } 210 | 211 | /* Assignment operator */ 212 | void operator=(const self_type &other) throw() { 213 | Py_INCREF(other.array); 214 | Py_DECREF(array); 215 | init_from_array(other.array); 216 | } 217 | 218 | /* Return the underlying Numpy array object. [Borrowed 219 | reference] */ 220 | PyObject* 221 | py_ptr() const throw() { 222 | return (PyObject*)array; 223 | } 224 | }; 225 | 226 | #endif 227 | --------------------------------------------------------------------------------