diff --git a/include/boost/histogram/axis.hpp b/include/boost/histogram/axis.hpp index 0c0ceb26..90b6a13e 100644 --- a/include/boost/histogram/axis.hpp +++ b/include/boost/histogram/axis.hpp @@ -27,6 +27,9 @@ #include #include +// forward declaration for serialization +namespace boost { namespace serialization { class access; }} + namespace boost { namespace histogram { @@ -136,8 +139,9 @@ private: int shape_ = 0; std::string label_; + friend class ::boost::serialization::access; template - friend void serialize(Archive &, axis_base &, unsigned); + void serialize(Archive &, unsigned); }; template <> class axis_base { @@ -185,8 +189,9 @@ private: int size_ = 0; std::string label_; + friend class ::boost::serialization::access; template - friend void serialize(Archive &, axis_base &, unsigned); + void serialize(Archive &, unsigned); }; namespace transform { @@ -281,14 +286,9 @@ public: private: value_type min_ = 0.0, delta_ = 1.0; - template class Transform1> - friend void serialize(Archive &, regular_axis &, - unsigned); - - // workaround for gcc-4.8 - template - friend void serialize(Archive &, regular_axis &, unsigned); + friend class ::boost::serialization::access; + template + void serialize(Archive &, unsigned); }; /** Axis for real-valued angles. @@ -350,8 +350,9 @@ public: private: value_type phase_ = 0.0, perimeter_ = 1.0; - template - friend void serialize(Archive &, circular_axis &, unsigned); + friend class ::boost::serialization::access; + template + void serialize(Archive &, unsigned); }; /** An axis for real-valued data and bins of varying width. @@ -442,8 +443,9 @@ public: private: std::unique_ptr x_; // smaller size compared to std::vector - template - friend void serialize(Archive &, variable_axis &, unsigned); + friend class ::boost::serialization::access; + template + void serialize(Archive &, unsigned); }; /** An axis for a contiguous range of integers. @@ -499,8 +501,9 @@ public: private: value_type min_ = 0; + friend class ::boost::serialization::access; template - friend void serialize(Archive &, integer_axis &, unsigned); + void serialize(Archive &, unsigned); }; /** An axis for enumerated categories. @@ -582,8 +585,9 @@ public: private: std::unique_ptr ptr_; + friend class ::boost::serialization::access; template - friend void serialize(Archive &, category_axis &, unsigned); + void serialize(Archive &, unsigned); }; using default_axes = mpl::vector, regular_axis, diff --git a/include/boost/histogram/histogram_impl_dynamic.hpp b/include/boost/histogram/histogram_impl_dynamic.hpp index e9940d35..b80f0de3 100644 --- a/include/boost/histogram/histogram_impl_dynamic.hpp +++ b/include/boost/histogram/histogram_impl_dynamic.hpp @@ -24,6 +24,12 @@ #include #include +// forward declaration for serialization +namespace boost { namespace serialization { class access; }} + +// forward declaration for python +namespace boost { namespace python { class access; }} + namespace boost { namespace histogram { @@ -274,12 +280,11 @@ private: return p; } - friend struct storage_access; - template friend class histogram; - template - friend void serialize(Archiv &, histogram &, unsigned); + friend class ::boost::python::access; + friend class ::boost::serialization::access; + template void serialize(Archive &, unsigned); }; template diff --git a/include/boost/histogram/histogram_impl_static.hpp b/include/boost/histogram/histogram_impl_static.hpp index f243278c..c6a0d05e 100644 --- a/include/boost/histogram/histogram_impl_static.hpp +++ b/include/boost/histogram/histogram_impl_static.hpp @@ -29,6 +29,9 @@ #include #include +// forward declaration for serialization +namespace boost { namespace serialization { class access; }} + namespace boost { namespace histogram { @@ -204,8 +207,8 @@ private: template friend class histogram; - template - friend void serialize(Archive &, histogram &, unsigned); + friend class ::boost::serialization::access; + template void serialize(Archive &, unsigned); }; /// default static type factory diff --git a/include/boost/histogram/serialization.hpp b/include/boost/histogram/serialization.hpp index a19f06d6..27484111 100644 --- a/include/boost/histogram/serialization.hpp +++ b/include/boost/histogram/serialization.hpp @@ -29,7 +29,7 @@ namespace histogram { namespace detail { template -inline void serialize(Archive &ar, weight &wt, unsigned /* version */) { +void serialize(Archive &ar, weight &wt, unsigned /* version */) { ar &wt.w; ar &wt.w2; } @@ -43,7 +43,7 @@ template struct serialize_helper { } // namespace detail template -inline void serialize(Archive &ar, container_storage &store, +void serialize(Archive &ar, container_storage &store, unsigned /* version */) { ar &store.c_; } @@ -118,84 +118,79 @@ void adaptive_storage::serialize(Archive &ar, } template -inline void serialize(Archive &ar, axis_base &base, - unsigned /* version */) { - ar &base.size_; - ar &base.label_; +void axis_base::serialize(Archive &ar, + unsigned /* version */) { + ar &size_; + ar &label_; } template -inline void serialize(Archive &ar, axis_base &base, +void axis_base::serialize(Archive &ar, unsigned /* version */) { - ar &base.size_; - ar &base.shape_; - ar &base.label_; + ar &size_; + ar &shape_; + ar &label_; } -template class Transform> -inline void serialize(Archive &ar, regular_axis &axis, - unsigned /* version */) { - ar &boost::serialization::base_object>(axis); - ar &axis.min_; - ar &axis.delta_; +template class Transform> +template +void regular_axis::serialize(Archive &ar, + unsigned /* version */) { + ar &boost::serialization::base_object>(*this); + ar &min_; + ar &delta_; } -// workaround for gcc-4.8 -template -inline void serialize(Archive &ar, regular_axis &axis, +template +template +void circular_axis::serialize(Archive &ar, unsigned /* version */) { - ar &boost::serialization::base_object>(axis); - ar &axis.min_; - ar &axis.delta_; + ar &boost::serialization::base_object>(*this); + ar &phase_; + ar &perimeter_; } -template -inline void serialize(Archive &ar, circular_axis &axis, +template +template +void variable_axis::serialize(Archive &ar, unsigned /* version */) { - ar &boost::serialization::base_object>(axis); - ar &axis.phase_; - ar &axis.perimeter_; -} - -template -inline void serialize(Archive &ar, variable_axis &axis, - unsigned /* version */) { - ar &boost::serialization::base_object>(axis); + ar &boost::serialization::base_object>(*this); if (Archive::is_loading::value) { - axis.x_.reset(new RealType[axis.bins() + 1]); + x_.reset(new RealType[bins() + 1]); } - ar &boost::serialization::make_array(axis.x_.get(), axis.bins() + 1); + ar &boost::serialization::make_array(x_.get(), bins() + 1); } template -inline void serialize(Archive &ar, integer_axis &axis, unsigned /* version */) { - ar &boost::serialization::base_object>(axis); - ar &axis.min_; +void integer_axis::serialize(Archive &ar, unsigned /* version */) { + ar &boost::serialization::base_object>(*this); + ar &min_; } template -inline void serialize(Archive &ar, category_axis &axis, +void category_axis::serialize(Archive &ar, unsigned /* version */) { - ar &boost::serialization::base_object>(axis); + ar &boost::serialization::base_object>(*this); if (Archive::is_loading::value) { - axis.ptr_.reset(new std::string[axis.bins()]); + ptr_.reset(new std::string[bins()]); } - ar &boost::serialization::make_array(axis.ptr_.get(), axis.bins()); + ar &boost::serialization::make_array(ptr_.get(), bins()); } -template -inline void serialize(Archive &ar, histogram &h, - unsigned /* version */) { +template +template +void histogram::serialize(Archive &ar, unsigned /* version */) { detail::serialize_helper sh(ar); - fusion::for_each(h.axes_, sh); - ar &h.storage_; + fusion::for_each(axes_, sh); + ar &storage_; } -template -inline void serialize(Archive &ar, histogram &h, +template +template +void histogram::serialize(Archive &ar, unsigned /* version */) { - ar &h.axes_; - ar &h.storage_; + ar &axes_; + ar &storage_; } } // namespace histogram diff --git a/include/boost/histogram/storage/adaptive_storage.hpp b/include/boost/histogram/storage/adaptive_storage.hpp index 39b5f753..dbf122cb 100644 --- a/include/boost/histogram/storage/adaptive_storage.hpp +++ b/include/boost/histogram/storage/adaptive_storage.hpp @@ -17,12 +17,11 @@ #include #include -// forward declaration for serialization workaround -namespace boost { -namespace serialization { -class access; -} // namespace serialization -} // namespace boost +// forward declaration for serialization +namespace boost { namespace serialization { class access; }} + +// forward declaration for python +namespace boost { namespace python { class access; }} namespace boost { namespace histogram { @@ -450,8 +449,7 @@ private: buffer_type buffer_; - friend struct storage_access; - // workaround for gcc-4.8 + friend class ::boost::python::access; friend class ::boost::serialization::access; template void serialize(Archive &, unsigned); }; diff --git a/include/boost/histogram/storage/container_storage.hpp b/include/boost/histogram/storage/container_storage.hpp index 78533099..9c66e36e 100644 --- a/include/boost/histogram/storage/container_storage.hpp +++ b/include/boost/histogram/storage/container_storage.hpp @@ -11,6 +11,9 @@ #include #include +// forward declaration for serialization +namespace boost { namespace serialization { class access; }} + namespace boost { namespace histogram { @@ -81,8 +84,9 @@ private: template friend class container_storage; - template - friend void serialize(Archive &, container_storage &, unsigned); + friend class ::boost::serialization::access; + template + void serialize(Archive &, unsigned); }; } // namespace histogram diff --git a/src/python/histogram.cpp b/src/python/histogram.cpp index ab70f0fb..2a21e867 100644 --- a/src/python/histogram.cpp +++ b/src/python/histogram.cpp @@ -27,16 +27,138 @@ #endif namespace boost { -namespace histogram { +namespace histogram { using dynamic_histogram = histogram>; +} // namespace histogram + +namespace python { #ifdef HAVE_NUMPY -auto array_cast = [](python::handle<>& h) { - return python::downcast(h.get()); +auto array_cast = [](handle<>& h) { + return downcast(h.get()); }; #endif +#ifdef HAVE_NUMPY +class access { +public: + using mp_int = histogram::adaptive_storage<>::mp_int; + using weight = histogram::adaptive_storage<>::weight; + template + using array = histogram::adaptive_storage<>::array; + + struct dtype_visitor : public static_visitor> { + template + std::pair operator()(const Array& /*unused*/) const { + std::pair p; + p.first = sizeof(typename Array::value_type); + p.second = str("|u") + str(p.first); + return p; + } + std::pair operator()(const array& /*unused*/) const { + std::pair p; + p.first = sizeof(uint8_t); + p.second = str("|u") + str(p.first); + return p; + } + std::pair operator()(const array& /*unused*/) const { + std::pair p; + p.first = sizeof(double); + p.second = str("|f") + str(p.first); + return p; + } + std::pair operator()(const array& /*unused*/) const { + std::pair p; + p.first = 0; // communicate that the type was array + p.second = str("|f") + str(sizeof(double)); + return p; + } + }; + + struct data_visitor : public static_visitor { + const list& shapes; + const list& strides; + data_visitor(const list& sh, const list& st) : shapes(sh), strides(st) {} + template + object operator()(const Array& b) const { + return make_tuple(reinterpret_cast(b.begin()), true); + } + object operator()(const array& b) const { + // cannot pass non-existent memory to numpy; make new + // zero-initialized uint8 array, and pass it + int dim = len(shapes); + npy_intp shapes2[BOOST_HISTOGRAM_AXIS_LIMIT]; + for (int i = 0; i < dim; ++i) { + shapes2[i] = extract(shapes[i]); + } + handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_UINT8)); + for (int i = 0; i < dim; ++i) { + PyArray_STRIDES(array_cast(a))[i] = extract(strides[i]); + } + auto *buf = static_cast(PyArray_DATA(array_cast(a))); + std::fill(buf, buf + b.size, uint8_t(0)); + PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); + return object(a); + } + object operator()(const array& b) const { + // cannot pass cpp_int to numpy; make new + // double array, fill it and pass it + int dim = len(shapes); + npy_intp shapes2[BOOST_HISTOGRAM_AXIS_LIMIT]; + for (int i = 0; i < dim; ++i) { + shapes2[i] = extract(shapes[i]); + } + handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_DOUBLE)); + for (int i = 0; i < dim; ++i) { + PyArray_STRIDES(array_cast(a))[i] = extract(strides[i]); + } + auto *buf = static_cast(PyArray_DATA(array_cast(a))); + for (std::size_t i = 0; i < b.size; ++i) { + buf[i] = static_cast(b[i]); + } + PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); + return object(a); + } + }; + + static object array_interface(const histogram::dynamic_histogram &self) { + dict d; + + list shapes; + list strides; + auto &b = self.storage_.buffer_; + auto dtype = apply_visitor(dtype_visitor(), b); + auto stride = dtype.first; + if (stride == 0) { // buffer is weight, needs special treatment + stride = sizeof(double); + strides.append(stride); + stride *= 2; + shapes.append(2); + } + for (unsigned i = 0; i < self.dim(); ++i) { + const auto s = shape(self.axis(i)); + shapes.append(s); + strides.append(stride); + stride *= s; + } + if (self.dim() == 0) { + shapes.append(0); + strides.append(stride); + } + d["shape"] = tuple(shapes); + d["strides"] = tuple(strides); + d["typestr"] = dtype.second; + d["data"] = apply_visitor(data_visitor(shapes, strides), b); + return d; + } +}; +#endif + +} // namespace python + +namespace histogram { + struct axis_visitor : public static_visitor { template python::object operator()(const T &t) const { return python::object(t); @@ -123,8 +245,8 @@ python::object histogram_fill(python::tuple args, python::dict kwargs) { // exception is thrown automatically if python::handle<> a(PyArray_FROM_OTF(o.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); - npy_intp *dims = PyArray_DIMS(array_cast(a)); - switch (PyArray_NDIM(array_cast(a))) { + npy_intp *dims = PyArray_DIMS(python::array_cast(a)); + switch (PyArray_NDIM(python::array_cast(a))) { case 1: if (self.dim() > 1) { PyErr_SetString(PyExc_ValueError, "array has to be two-dimensional"); @@ -150,20 +272,20 @@ python::object histogram_fill(python::tuple args, python::dict kwargs) { python::handle<> aw( PyArray_FROM_OTF(ow.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY)); - if (PyArray_NDIM(array_cast(aw)) != 1) { + if (PyArray_NDIM(python::array_cast(aw)) != 1) { PyErr_SetString(PyExc_ValueError, "array has to be one-dimensional"); python::throw_error_already_set(); } - if (PyArray_DIMS(array_cast(aw))[0] != dims[0]) { + if (PyArray_DIMS(python::array_cast(aw))[0] != dims[0]) { PyErr_SetString(PyExc_ValueError, "sizes do not match"); python::throw_error_already_set(); } for (unsigned i = 0; i < dims[0]; ++i) { - double *v = reinterpret_cast(PyArray_GETPTR1(array_cast(a), i)); - double *w = reinterpret_cast(PyArray_GETPTR1(array_cast(aw), i)); + double *v = reinterpret_cast(PyArray_GETPTR1(python::array_cast(a), i)); + double *w = reinterpret_cast(PyArray_GETPTR1(python::array_cast(aw), i)); self.wfill(*w, v, v + self.dim()); } @@ -173,7 +295,7 @@ python::object histogram_fill(python::tuple args, python::dict kwargs) { } } else { for (unsigned i = 0; i < dims[0]; ++i) { - double *v = reinterpret_cast(PyArray_GETPTR1(array_cast(a), i)); + double *v = reinterpret_cast(PyArray_GETPTR1(python::array_cast(a), i)); self.fill(v, v + self.dim()); } } @@ -273,120 +395,6 @@ std::string histogram_repr(const dynamic_histogram &h) { return os.str(); } -#ifdef HAVE_NUMPY -struct storage_access { - using mp_int = adaptive_storage<>::mp_int; - using weight = adaptive_storage<>::weight; - template - using array = adaptive_storage<>::array; - - struct dtype_visitor : public static_visitor> { - template - std::pair operator()(const Array& /*unused*/) const { - std::pair p; - p.first = sizeof(typename Array::value_type); - p.second = python::str("|u") + python::str(p.first); - return p; - } - std::pair operator()(const array& /*unused*/) const { - std::pair p; - p.first = sizeof(uint8_t); - p.second = python::str("|u") + python::str(p.first); - return p; - } - std::pair operator()(const array& /*unused*/) const { - std::pair p; - p.first = sizeof(double); - p.second = python::str("|f") + python::str(p.first); - return p; - } - std::pair operator()(const array& /*unused*/) const { - std::pair p; - p.first = 0; // communicate that the type was array - p.second = python::str("|f") + python::str(sizeof(double)); - return p; - } - }; - - struct data_visitor : public static_visitor { - const python::list& shapes; - const python::list& strides; - data_visitor(const python::list& sh, const python::list& st) : shapes(sh), strides(st) {} - template - python::object operator()(const Array& b) const { - return python::make_tuple(reinterpret_cast(b.begin()), true); - } - python::object operator()(const array& b) const { - // cannot pass non-existent memory to numpy; make new - // zero-initialized uint8 array, and pass it - int dim = python::len(shapes); - npy_intp shapes2[BOOST_HISTOGRAM_AXIS_LIMIT]; - for (int i = 0; i < dim; ++i) { - shapes2[i] = python::extract(shapes[i]); - } - python::handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_UINT8)); - for (int i = 0; i < dim; ++i) { - PyArray_STRIDES(array_cast(a))[i] = python::extract(strides[i]); - } - auto *buf = static_cast(PyArray_DATA(array_cast(a))); - std::fill(buf, buf + b.size, uint8_t(0)); - PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); - return python::object(a); - } - python::object operator()(const array& b) const { - // cannot pass cpp_int to numpy; make new - // double array, fill it and pass it - int dim = python::len(shapes); - npy_intp shapes2[BOOST_HISTOGRAM_AXIS_LIMIT]; - for (int i = 0; i < dim; ++i) { - shapes2[i] = python::extract(shapes[i]); - } - python::handle<> a(PyArray_SimpleNew(dim, shapes2, NPY_DOUBLE)); - for (int i = 0; i < dim; ++i) { - PyArray_STRIDES(array_cast(a))[i] = python::extract(strides[i]); - } - auto *buf = static_cast(PyArray_DATA(array_cast(a))); - for (std::size_t i = 0; i < b.size; ++i) { - buf[i] = static_cast(b[i]); - } - PyArray_CLEARFLAGS(array_cast(a), NPY_ARRAY_WRITEABLE); - return python::object(a); - } - }; - - static python::object array_interface(const dynamic_histogram &self) { - python::dict d; - - python::list shapes; - python::list strides; - auto &b = self.storage_.buffer_; - auto dtype = apply_visitor(dtype_visitor(), b); - auto stride = dtype.first; - if (stride == 0) { // buffer is weight, needs special treatment - stride = sizeof(double); - strides.append(stride); - stride *= 2; - shapes.append(2); - } - for (unsigned i = 0; i < self.dim(); ++i) { - const auto s = shape(self.axis(i)); - shapes.append(s); - strides.append(stride); - stride *= s; - } - if (self.dim() == 0) { - shapes.append(0); - strides.append(stride); - } - d["shape"] = python::tuple(shapes); - d["strides"] = python::tuple(strides); - d["typestr"] = dtype.second; - d["data"] = apply_visitor(data_visitor(shapes, strides), b); - return d; - } -}; -#endif - void register_histogram() { python::docstring_options dopt(true, true, false); @@ -399,7 +407,7 @@ void register_histogram() { // shadowed C++ ctors .def(python::init()) #ifdef HAVE_NUMPY - .add_property("__array_interface__", &storage_access::array_interface) + .add_property("__array_interface__", &python::access::array_interface) #endif .def("__len__", &dynamic_histogram::dim) .def("__getitem__", histogram_axis) diff --git a/test/adaptive_storage_test.cpp b/test/adaptive_storage_test.cpp index 1ad8829c..bcf1de29 100644 --- a/test/adaptive_storage_test.cpp +++ b/test/adaptive_storage_test.cpp @@ -14,6 +14,7 @@ #include namespace boost { + namespace histogram { template adaptive_storage<> prepare(unsigned n = 1) { @@ -48,13 +49,20 @@ template <> adaptive_storage<> prepare(unsigned n) { return s; } -struct storage_access { - template static adaptive_storage<> set_value(unsigned n, T x) { - adaptive_storage<> s = prepare(n); - get::array>(s.buffer_)[0] = x; +} // namespace prepare + +namespace python { // cheating to get access +class access { +public: + template static histogram::adaptive_storage<> set_value(unsigned n, T x) { + histogram::adaptive_storage<> s = histogram::prepare(n); + get::array>(s.buffer_)[0] = x; return s; } }; +} // namespace python + +namespace histogram { template void copy_impl() { const auto b = prepare(1); @@ -74,7 +82,7 @@ template void copy_impl() { } template void serialization_impl() { - const auto a = storage_access::set_value(1, T(1)); + const auto a = python::access::set_value(1, T(1)); std::ostringstream os; std::string buf; { @@ -115,7 +123,7 @@ template <> void serialization_impl() { template void equal_impl() { adaptive_storage<> a(1); - auto b = storage_access::set_value(1, T(0)); + auto b = python::access::set_value(1, T(0)); BOOST_TEST_EQ(a.value(0), 0.0); BOOST_TEST_EQ(a.variance(0), 0.0); BOOST_TEST(a == b); @@ -123,7 +131,7 @@ template void equal_impl() { BOOST_TEST(!(a == b)); container_storage> c(1); - auto d = storage_access::set_value(1, T(0)); + auto d = python::access::set_value(1, T(0)); BOOST_TEST(c == d); c.increase(0); BOOST_TEST(!(c == d)); @@ -131,8 +139,8 @@ template void equal_impl() { template <> void equal_impl() { adaptive_storage<> a(1); - auto b = storage_access::set_value(1, uint8_t(0)); - auto c = storage_access::set_value(2, uint8_t(0)); + auto b = python::access::set_value(1, uint8_t(0)); + auto c = python::access::set_value(2, uint8_t(0)); auto d = container_storage>(1); BOOST_TEST_EQ(a.value(0), 0.0); BOOST_TEST_EQ(a.variance(0), 0.0); @@ -152,7 +160,7 @@ template <> void equal_impl() { template void increase_and_grow_impl() { auto tmax = std::numeric_limits::max(); - auto s = storage_access::set_value(2, tmax - 1); + auto s = python::access::set_value(2, tmax - 1); auto n = s; auto n2 = s; @@ -180,7 +188,7 @@ template <> void increase_and_grow_impl() { } template void convert_container_storage_impl() { - const auto aref = storage_access::set_value(1, T(0)); + const auto aref = python::access::set_value(1, T(0)); container_storage> s(1); s.increase(0); @@ -306,7 +314,7 @@ int main() { increase_and_grow_impl(); // only increase for mp_int - auto a = storage_access::set_value(2, detail::mp_int(1)); + auto a = boost::python::access::set_value(2, detail::mp_int(1)); BOOST_TEST_EQ(a.value(0), 1.0); BOOST_TEST_EQ(a.value(1), 0.0); a.increase(0);