HiPipe  0.7.0
C++17 data pipeline with Python bindings.
vector_converter.hpp
1 /****************************************************************************
2  * hipipe library
3  * Copyright (c) 2017, Cognexa Solutions s.r.o.
4  * Copyright (c) 2018, Iterait a.s.
5  * Author(s) Filip Matzner
6  *
7  * This file is distributed under the MIT License.
8  * See the accompanying file LICENSE.txt for the complete license agreement.
9  ****************************************************************************/
10 
11 #pragma once
12 #include <hipipe/build_config.hpp>
13 #ifdef HIPIPE_BUILD_PYTHON
14 
15 // this header has to be included before the numpy header
16 #include <hipipe/core/python/initialize.hpp>
17 
18 #include <Python.h>
19 #include <boost/python.hpp>
20 #include <numpy/ndarrayobject.h>
21 
22 #include <stdexcept>
23 #include <vector>
24 
25 namespace hipipe::python::utility {
26 
27 namespace detail {
28 
29  template<typename T>
30  struct to_ndarray_trait
31  {
32  using type_t = PyObject*;
33 
34  static PyObject* convert(T val)
35  {
36  boost::python::object obj{std::move(val)};
37  Py_INCREF(obj.ptr());
38  return obj.ptr();
39  }
40 
41  static int typenum()
42  {
43  return NPY_OBJECT;
44  }
45  };
46 
47 #define HIPIPE_DEFINE_TO_NDARRAY_TRAIT(C_TYPE, NP_TYPE, TYPENUM) \
48  template<> \
49  struct to_ndarray_trait<C_TYPE> \
50  { \
51  using type_t = NP_TYPE; \
52  \
53  static NP_TYPE convert(C_TYPE val) \
54  { \
55  return val; \
56  } \
57  \
58  static int typenum() \
59  { \
60  return TYPENUM; \
61  } \
62  }
63 
64  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(bool, npy_bool, NPY_BOOL);
65  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::int8_t, npy_byte, NPY_BYTE);
66  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::uint8_t, npy_ubyte, NPY_UBYTE);
67  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::int16_t, npy_int16, NPY_INT16);
68  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::uint16_t, npy_uint16, NPY_UINT16);
69  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::int32_t, npy_int32, NPY_INT32);
70  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::uint32_t, npy_uint32, NPY_UINT32);
71  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::int64_t, npy_int64, NPY_INT64);
72  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(std::uint64_t, npy_uint64, NPY_UINT64);
73  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(float, npy_float, NPY_FLOAT);
74  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(double, npy_double, NPY_DOUBLE);
75  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(long double, npy_longdouble, NPY_LONGDOUBLE);
76  HIPIPE_DEFINE_TO_NDARRAY_TRAIT(PyObject*, PyObject*, NPY_OBJECT);
77 
78 #undef HIPIPE_DEFINE_TO_NDARRAY_TRAIT
79 
85  template<typename T>
86  auto to_ndarray_element(T val)
87  {
88  return detail::to_ndarray_trait<T>::convert(std::move(val));
89  }
90 
93  template<typename T>
94  using ndarray_type_t = typename detail::to_ndarray_trait<T>::type_t;
95 
98  template<typename T>
99  int to_ndarray_typenum()
100  {
101  return detail::to_ndarray_trait<T>::typenum();
102  }
103 
104 } // namespace detail
105 
108 template<typename T>
109 PyObject* to_ndarray(std::vector<T> vec)
110 {
111  auto data = std::make_unique<detail::ndarray_type_t<T>[]>(vec.size());
112  for (std::size_t i = 0; i < vec.size(); ++i) {
113  // vector<bool> is special as usual :-/
114  if constexpr(std::is_same_v<T, bool>) {
115  data[i] = detail::to_ndarray_element((bool)vec[i]);
116  } else {
117  data[i] = detail::to_ndarray_element(std::move(vec[i]));
118  }
119  }
120  npy_intp dims[1]{static_cast<npy_intp>(vec.size())};
121 
122  PyObject* arr = PyArray_SimpleNewFromData(
123  1, dims, detail::to_ndarray_typenum<T>(), reinterpret_cast<void*>(data.release()));
124  if (!arr) throw std::runtime_error{"Cannot create Python NumPy ndarray."};
125  // we have to tell NumPy to delete the data when the array is removed
126  PyArray_ENABLEFLAGS(reinterpret_cast<PyArrayObject*>(arr), NPY_ARRAY_OWNDATA);
127  return arr;
128 }
129 
130 } // namespace hipipe::python::utility
131 
132 #endif // HIPIPE_BUILD_PYTHON