diff --git a/examples/guide_histogram_operators.cpp b/examples/guide_histogram_operators.cpp index 2c54b12a..e6c0fbd2 100644 --- a/examples/guide_histogram_operators.cpp +++ b/examples/guide_histogram_operators.cpp @@ -37,7 +37,10 @@ int main() { // note special effect of multiplication on weight_counter variance auto h = bh::make_histogram_with(std::vector>(), bh::axis::regular<>(2, -1, 1)); - h(-0.5); // counts are: 1 0 + h(-0.5); + + // counts are: 1 0 + assert(h.at(0).value() == 1 && h.at(1).value() == 0); auto h_sum = h + h; auto h_mul = 2 * h; diff --git a/include/boost/histogram/adaptive_storage.hpp b/include/boost/histogram/adaptive_storage.hpp index 563e834d..55c0649d 100644 --- a/include/boost/histogram/adaptive_storage.hpp +++ b/include/boost/histogram/adaptive_storage.hpp @@ -92,7 +92,7 @@ bool safe_radd(T& t, const boost::multiprecision::number& u) { } // namespace detail template -struct adaptive_storage { +struct adaptive_storage : storage_tag { static_assert( std::is_same::pointer, typename std::allocator_traits::value_type*>::value, diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 84ab18b6..04e8bc36 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -147,9 +147,6 @@ void range_check(const std::tuple&) { static_assert(N < sizeof...(Ts), "index out of range"); } -template -using axis_at = mp_at_c::value * N>; - template > auto axis_get(T&& axes) -> decltype(std::get(std::forward(axes))) { return std::get(std::forward(axes)); diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 4571b42d..59dbff38 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -81,9 +82,7 @@ constexpr decltype(auto) static_if(Ts&&... ts) { template \ struct name##_impl { \ template \ - struct SFINAE {}; \ - template \ - static std::true_type Test(SFINAE*); \ + static std::true_type Test(void*); \ template \ static std::false_type Test(...); \ using type = decltype(Test(nullptr)); \ @@ -97,7 +96,7 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support, BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval().value(0))); // TODO try casting to more specific pmf with correct return type -BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, &T::options); +BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, (std::declval().options())); BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); @@ -110,17 +109,26 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_clear, &T::clear); BOOST_HISTOGRAM_MAKE_SFINAE(has_allocator, &T::get_allocator); -BOOST_HISTOGRAM_MAKE_SFINAE(is_storage, (std::declval())); - BOOST_HISTOGRAM_MAKE_SFINAE(is_indexable, (std::declval()[0])); BOOST_HISTOGRAM_MAKE_SFINAE(is_transform, (&T::forward, &T::inverse)); -BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container, (std::declval()[0], &T::size, - std::begin(std::declval()), - std::end(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_vector_like, (std::declval()[0], &T::size, &T::clear, + &T::reserve, &T::cbegin, &T::cend)); -BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval()))); +BOOST_HISTOGRAM_MAKE_SFINAE(is_array_like, + (std::declval()[0], &T::size, std::tuple_size::value, + &T::cbegin, &T::cend)); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_map_like, + (typename T::key_type(), typename T::mapped_type(), + std::declval().begin(), std::declval().end())); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_indexable_container, (std::declval()[0], &T::size, + std::begin(std::declval()), + std::end(std::declval()))); + +BOOST_HISTOGRAM_MAKE_SFINAE(is_tuple, (std::get<0>(std::declval()))); BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable, (std::declval() == std::declval())); @@ -135,6 +143,9 @@ BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable, BOOST_HISTOGRAM_MAKE_SFINAE(is_callable, (std::declval()())); +template +using is_storage = typename std::is_base_of::type; + namespace { template struct is_axis_variant_impl : std::false_type {}; @@ -151,7 +162,7 @@ using is_axis_or_axis_variant = mp11::mp_or, is_axis_variant>; template > using is_axis_vector = - mp11::mp_all>, is_axis_or_axis_variant>; + mp11::mp_all>, is_axis_or_axis_variant>; struct static_container_tag {}; struct iterable_container_tag {}; @@ -160,7 +171,7 @@ struct no_container_tag {}; template using classify_container = typename std::conditional< is_iterable::value, iterable_container_tag, - typename std::conditional::value, static_container_tag, + typename std::conditional::value, static_container_tag, no_container_tag>::type>::type; namespace { @@ -188,7 +199,7 @@ struct requires_iterator {}; template , void>> struct requires_iterable {}; -template , void>> +template , void>> struct requires_static_container {}; template , void>> diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 9ff9a537..e057342f 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -54,6 +54,8 @@ template class variant; } // namespace axis +struct storage_tag {}; + template struct storage_adaptor; diff --git a/include/boost/histogram/storage_adaptor.hpp b/include/boost/histogram/storage_adaptor.hpp index 55986e85..2db89a05 100644 --- a/include/boost/histogram/storage_adaptor.hpp +++ b/include/boost/histogram/storage_adaptor.hpp @@ -20,70 +20,131 @@ namespace boost { namespace histogram { namespace detail { -template -void increment_element_impl(std::true_type, T& t) { - t(); -} + +template +struct element_adaptor_impl; template -void increment_element_impl(std::false_type, T& t) { - ++t; -} - -template -void increment_element(T& t) { - increment_element_impl(is_callable(), t); -} - -template -struct storage_reset_impl; - -template -struct storage_reset_impl : T { - using T::T; - storage_reset_impl(const T& t) : T(t) {} - storage_reset_impl(T&& t) : T(std::move(t)) {} - - void reset(T& t, std::size_t n) { - t.resize(n); - std::fill(t.begin(), t.end(), typename T::value_type()); +struct element_adaptor_impl { + static void inc(T& t) { t(); } + template + static void add(T& t, U&& u) { + t(std::forward(u)); } }; -template -struct storage_reset_impl : T { - using T::T; - storage_reset_impl(const T& t) : T(t) {} - storage_reset_impl(T&& t) : T(std::move(t)) {} +template +struct element_adaptor_impl { + static void inc(T& t) { ++t; } + template + static void add(T& t, U&& u) { + t += std::forward(u); + } +}; - void reset(T& t, std::size_t n) { +template +using element_adaptor = element_adaptor_impl, T>; + +template +struct augmentation_error {}; + +template +struct vector_augmentation : T { + using value_type = typename T::value_type; + + using T::T; + vector_augmentation(const T& t) : T(t) {} + vector_augmentation(T&& t) : T(std::move(t)) {} + + void reset(std::size_t n) { + this->resize(n); + std::fill(this->begin(), this->end(), value_type()); + } + + template + void set(std::size_t i, U&& u) { + (*this)[i] = std::forward(u); + } +}; + +template +struct array_augmentation : T { + using value_type = typename T::value_type; + + using T::T; + array_augmentation(const T& t) : T(t) {} + array_augmentation(T&& t) : T(std::move(t)) {} + + void reset(std::size_t n) { if (n > this->max_size()) // for std::array throw std::runtime_error( - detail::cat("size ", n, " exceeds maximum capacity ", t.max_size())); - // map-like has clear(), std::array does not - static_if( - [](auto& t) { t.clear(); }, - [n](auto& t) { std::fill_n(t.begin(), n, typename T::value_type()); }, t); + detail::cat("size ", n, " exceeds maximum capacity ", this->max_size())); + std::fill_n(this->begin(), n, value_type()); size_ = n; } + template + void set(std::size_t i, U&& u) { + (*this)[i] = std::forward(u); + } + std::size_t size_ = 0; std::size_t size() const { return size_; } }; template -using storage_reset = storage_reset_impl, has_method_clear, T>; +struct map_augmentation : T { + static_assert(std::is_same::value, + "map must have key_type equal to std::size_t"); + using value_type = typename T::mapped_type; + + using T::T; + map_augmentation(const T& t) : T(t) {} + map_augmentation(T&& t) : T(std::move(t)) {} + + void reset(std::size_t n) { + this->clear(); + size_ = n; + } + + value_type operator[](std::size_t i) const { + auto it = this->find(i); + return it == this->end() ? value_type() : *it; + } + + template + void set(std::size_t i, U&& u) { + auto it = this->find(i); + if (u == value_type()) { + if (it != this->end()) this->erase(it); + } else if (it != this->end()) + *it = std::forward(u); + else + this->insert(i, std::forward(u)); + } + + std::size_t size_ = 0; + std::size_t size() const { return size_; } +}; + +template +using storage_augmentation = mp11::mp_if< + is_vector_like, vector_augmentation, + mp11::mp_if, array_augmentation, + mp11::mp_if, map_augmentation, augmentation_error>>>; + } // namespace detail /// generic implementation for std::array, vector-like, and map-like containers template -struct storage_adaptor : detail::storage_reset { - using base_type = detail::storage_reset; - using base_type::base_type; - using value_type = typename T::value_type; - using const_reference = typename T::const_reference; - struct storage_tag {}; +struct storage_adaptor : detail::storage_augmentation, storage_tag { + using base_type = detail::storage_augmentation; + using value_type = typename base_type::value_type; + using element_adaptor = detail::element_adaptor; + using const_reference = const value_type&; + + using base_type::base_type; storage_adaptor() = default; storage_adaptor(const storage_adaptor&) = default; storage_adaptor& operator=(const storage_adaptor&) = default; @@ -100,22 +161,20 @@ struct storage_adaptor : detail::storage_reset { template > storage_adaptor& operator=(const U& rhs) { - reset(rhs.size()); - for (std::size_t i = 0, n = this->size(); i < n; ++i) (*this)(i, rhs[i]); + this->reset(rhs.size()); + for (std::size_t i = 0, n = this->size(); i < n; ++i) this->set(i, rhs[i]); return *this; } - void reset(std::size_t n) { detail::storage_reset::reset(*this, n); } - void operator()(std::size_t i) { BOOST_ASSERT(i < this->size()); - detail::increment_element((*this)[i]); + element_adaptor::inc((*this)[i]); } template void operator()(std::size_t i, U&& u) { BOOST_ASSERT(i < this->size()); - (*this)[i] += std::forward(u); + element_adaptor::add((*this)[i], std::forward(u)); } // precondition: storages have equal size @@ -123,7 +182,7 @@ struct storage_adaptor : detail::storage_reset { storage_adaptor& operator+=(const U& u) { const auto n = this->size(); BOOST_ASSERT_MSG(n == u.size(), "sizes must be equal"); - for (std::size_t i = 0; i < n; ++i) (*this)(i, u[i]); + for (std::size_t i = 0; i < n; ++i) element_adaptor::add((*this)[i], u[i]); return *this; } @@ -139,7 +198,7 @@ struct storage_adaptor : detail::storage_reset { const auto n = this->size(); if (n != u.size()) return false; for (std::size_t i = 0; i < n; ++i) - if (!((*this)[i] == u[i])) return false; + if ((*this)[i] != u[i]) return false; return true; } }; diff --git a/test/adaptive_storage_test.cpp b/test/adaptive_storage_test.cpp index 322e517a..8f7d993a 100644 --- a/test/adaptive_storage_test.cpp +++ b/test/adaptive_storage_test.cpp @@ -16,12 +16,6 @@ namespace bh = boost::histogram; using adaptive_storage_type = bh::adaptive_storage<>; template using vector_storage = bh::storage_adaptor>; -template -using array_storage = bh::storage_adaptor>; -template -using map_storage = bh::storage_adaptor>; - -// TODO: test array, map, boost::accumulator as bin type using bh::weight; diff --git a/test/axis_category_test.cpp b/test/axis_category_test.cpp index 6a3c15b5..cbc67ea0 100644 --- a/test/axis_category_test.cpp +++ b/test/axis_category_test.cpp @@ -6,14 +6,14 @@ #include #include -#include #include +#include #include #include #include #include #include -#include "utility.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; diff --git a/test/axis_circular_test.cpp b/test/axis_circular_test.cpp index 97a39086..8e3269ea 100644 --- a/test/axis_circular_test.cpp +++ b/test/axis_circular_test.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; @@ -48,9 +48,7 @@ int main() { } // iterators - { - test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); - } + { test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); } return boost::report_errors(); } diff --git a/test/axis_integer_test.cpp b/test/axis_integer_test.cpp index b45f73d7..857d77db 100644 --- a/test/axis_integer_test.cpp +++ b/test/axis_integer_test.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; @@ -71,9 +71,7 @@ int main() { } // iterators - { - test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); - } + { test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); } return boost::report_errors(); } diff --git a/test/axis_regular_test.cpp b/test/axis_regular_test.cpp index e2392842..312c2d47 100644 --- a/test/axis_regular_test.cpp +++ b/test/axis_regular_test.cpp @@ -13,7 +13,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; diff --git a/test/axis_variable_test.cpp b/test/axis_variable_test.cpp index 719ef1f6..6203f4d0 100644 --- a/test/axis_variable_test.cpp +++ b/test/axis_variable_test.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; @@ -52,9 +52,7 @@ int main() { } // iterators - { - test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); - } + { test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); } return boost::report_errors(); } diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp index ae916872..e2dd04fa 100644 --- a/test/axis_variant_test.cpp +++ b/test/axis_variant_test.cpp @@ -18,20 +18,17 @@ #include #include #include -#include "utility.hpp" +#include "utility_allocator.hpp" +#include "utility_axis.hpp" using namespace boost::histogram; int main() { - { - BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); - } + { BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); } { - axis::variant< - axis::integer<>, - axis::category - > a{axis::integer<>(0, 2, "int")}; + axis::variant, axis::category> a{ + axis::integer<>(0, 2, "int")}; BOOST_TEST_EQ(a(-10), -1); BOOST_TEST_EQ(a(-1), -1); BOOST_TEST_EQ(a(0), 0); @@ -178,7 +175,7 @@ int main() { { auto a = axis::variant>(axis::category<>({2, 1, 3})); - BOOST_TEST_THROWS(a[0].lower(), std::runtime_error); + BOOST_TEST_THROWS(a[0].lower(), std::runtime_error); } // vector of axes with custom allocators @@ -206,22 +203,22 @@ int main() { axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); } // 5 axis::variant objects - BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); - BOOST_TEST_EQ(db[typeid(axis_type)].first, 5); + BOOST_TEST_EQ(db.at().first, db.at().second); + BOOST_TEST_EQ(db.at().first, 5); // label - BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_EQ(db[typeid(char)].first, 3u); + BOOST_TEST_EQ(db.at().first, db.at().second); + BOOST_TEST_EQ(db.at().first, 3u); // nothing to allocate for T1 // nothing to allocate for T2 // T3 allocates storage for bin edges - BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second); - BOOST_TEST_EQ(db[typeid(double)].first, 3u); + BOOST_TEST_EQ(db.at().first, db.at().second); + BOOST_TEST_EQ(db.at().first, 3u); // nothing to allocate for T4 // T5 allocates storage for long array - BOOST_TEST_EQ(db[typeid(long)].first, db[typeid(long)].second); - BOOST_TEST_EQ(db[typeid(long)].first, 5u); + BOOST_TEST_EQ(db.at().first, db.at().second); + BOOST_TEST_EQ(db.at().first, 5u); #if (BOOST_MSVC) BOOST_TEST_EQ(db.size(), 5); // axis_type, char, double, long + ??? @@ -231,9 +228,7 @@ int main() { } // iterators - { - test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); - } + { test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); } return boost::report_errors(); } diff --git a/test/detail_test.cpp b/test/detail_test.cpp index 02fa569f..6f45ed3a 100644 --- a/test/detail_test.cpp +++ b/test/detail_test.cpp @@ -6,17 +6,17 @@ #include #include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include #include -#include #include -#include "utility.hpp" +#include +#include "utility_meta.hpp" using namespace boost::histogram; diff --git a/test/histogram_dynamic_test.cpp b/test/histogram_dynamic_test.cpp index a9517aa4..8ff06dee 100644 --- a/test/histogram_dynamic_test.cpp +++ b/test/histogram_dynamic_test.cpp @@ -7,10 +7,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -20,7 +20,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_histogram.hpp" using namespace boost::histogram; using namespace boost::histogram::literals; // to get _c suffix diff --git a/test/histogram_mixed_test.cpp b/test/histogram_mixed_test.cpp index 5817ebf6..6fac5507 100644 --- a/test/histogram_mixed_test.cpp +++ b/test/histogram_mixed_test.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_histogram.hpp" using namespace boost::histogram; diff --git a/test/histogram_serialization_test.cpp b/test/histogram_serialization_test.cpp index b25bef24..6a3528dc 100644 --- a/test/histogram_serialization_test.cpp +++ b/test/histogram_serialization_test.cpp @@ -13,7 +13,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_histogram.hpp" using namespace boost::histogram; diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 9ffcd9ae..ea5be1d2 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -5,11 +5,11 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #include #include #include -#include #include #include #include @@ -17,7 +17,9 @@ #include #include #include -#include "utility.hpp" +#include "utility_allocator.hpp" +#include "utility_histogram.hpp" +#include "utility_meta.hpp" using namespace boost::histogram; using namespace boost::histogram::literals; // to get _c suffix @@ -102,8 +104,7 @@ void run_tests() { auto h2 = decltype(h)(h); BOOST_TEST(h2 == h); auto h3 = - histogram, axis::integer<>>, std::vector>( - h); + histogram, axis::integer<>>, std::vector>(h); BOOST_TEST_EQ(h3, h); } @@ -118,8 +119,8 @@ void run_tests() { // test self-assign h2 = h2; BOOST_TEST_EQ(h, h2); - auto h3 = histogram, axis::integer<>>, - std::vector>(); + auto h3 = + histogram, axis::integer<>>, std::vector>(); h3 = h; BOOST_TEST_EQ(h, h3); } @@ -812,13 +813,13 @@ void run_tests() { } // int allocation for std::vector - BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second); - BOOST_TEST_EQ(db[typeid(int)].first, 1002u); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(int)].first, db[&BOOST_CORE_TYPEID(int)].second); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(int)].first, 1002u); if (Tag()) { // axis::variant allocation, only for dynamic histogram using T = axis::variant>; - BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second); - BOOST_TEST_LE(db[typeid(T)].first, + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(T)].first, db[&BOOST_CORE_TYPEID(T)].second); + BOOST_TEST_LE(db[&BOOST_CORE_TYPEID(T)].first, 1u); // zero if vector uses small-vector-optimisation } } diff --git a/test/index_mapper_test.cpp b/test/index_mapper_test.cpp index e0612fe4..333ab8b2 100644 --- a/test/index_mapper_test.cpp +++ b/test/index_mapper_test.cpp @@ -8,7 +8,6 @@ #include #include #include -#include "utility.hpp" using namespace boost::histogram::detail; diff --git a/test/meta_test.cpp b/test/meta_test.cpp index d2e7d73f..883861ae 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -18,9 +18,10 @@ #include #include #include +#include #include #include -#include "utility.hpp" +#include "utility_meta.hpp" namespace bh = boost::histogram; using namespace bh::detail; @@ -83,9 +84,11 @@ int main() { { struct A {}; struct B { - bh::axis::option_type options(); + bh::axis::option_type options() { return bh::axis::option_type(); } }; + auto foo = static_cast>(&B::options); + BOOST_TEST_TRAIT_FALSE((has_method_options)); BOOST_TEST_TRAIT_TRUE((has_method_options)); } @@ -183,8 +186,42 @@ int main() { BOOST_TEST_TRAIT_TRUE((is_transform)); } + // is_vector_like { + struct A {}; + using B = std::vector; + using C = std::array; + using D = std::map; + BOOST_TEST_TRAIT_FALSE((is_vector_like)); + BOOST_TEST_TRAIT_TRUE((is_vector_like)); + BOOST_TEST_TRAIT_FALSE((is_vector_like)); + BOOST_TEST_TRAIT_FALSE((is_vector_like)); + } + // is_array_like + { + struct A {}; + using B = std::vector; + using C = std::array; + using D = std::map; + BOOST_TEST_TRAIT_FALSE((is_array_like)); + BOOST_TEST_TRAIT_FALSE((is_array_like)); + BOOST_TEST_TRAIT_TRUE((is_array_like)); + BOOST_TEST_TRAIT_FALSE((is_array_like)); + } + + // is_map_like + { + struct A {}; + using B = std::vector; + using C = std::array; + using D = std::map; + using E = std::unordered_map; + BOOST_TEST_TRAIT_FALSE((is_map_like)); + BOOST_TEST_TRAIT_FALSE((is_map_like)); + BOOST_TEST_TRAIT_FALSE((is_map_like)); + BOOST_TEST_TRAIT_TRUE((is_map_like)); + BOOST_TEST_TRAIT_TRUE((is_map_like)); } // is_equal_comparable diff --git a/test/speed_cpp.cpp b/test/speed_cpp.cpp index 798a8e2a..6543b812 100644 --- a/test/speed_cpp.cpp +++ b/test/speed_cpp.cpp @@ -12,7 +12,7 @@ #include #include #include -#include "utility.hpp" +#include "utility_histgram.hpp" using namespace boost::histogram; using boost::mp11::mp_list; diff --git a/test/storage_adaptor_test.cpp b/test/storage_adaptor_test.cpp index 886151e4..d06b6c49 100644 --- a/test/storage_adaptor_test.cpp +++ b/test/storage_adaptor_test.cpp @@ -15,36 +15,136 @@ using namespace boost::histogram; +// TODO: test array, map, boost::accumulator as bin type template -using vector_storage = storage_adaptor>; -template -using array_storage = storage_adaptor>; - -int main() { +void tests() { // ctor, copy, move { - vector_storage a; - vector_storage b(a); - vector_storage c; + storage_adaptor a; + a.reset(2); + storage_adaptor b(a); + storage_adaptor c; c = a; - vector_storage d(std::move(a)); - vector_storage e; - e = std::move(b); - vector_storage f(e); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(c.size(), 2); - vector_storage g(std::vector(10, 0)); + storage_adaptor d(std::move(a)); + BOOST_TEST_EQ(d.size(), 2); + storage_adaptor e; + e = std::move(d); + BOOST_TEST_EQ(e.size(), 2); - array_storage h; - h = g; - array_storage i(g); + T t; + storage_adaptor g(t); // tests converting ctor } // increment and reset { - vector_storage a, b; + storage_adaptor a, b; + a.reset(1); + b.reset(2); + a(0); + a(0); + b(0); + b(0, 2); + b(1, 5); + BOOST_TEST_EQ(a[0], 2); + BOOST_TEST_EQ(b[0], 3); + BOOST_TEST_EQ(b[1], 5); + a.reset(2); + b.reset(1); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 0); + BOOST_TEST_EQ(b[0], 0); + } + + // multiply + { + storage_adaptor a; + a.reset(2); + a(0); + a *= 3; + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 0); + a(1, 2); + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 2); + a *= 3; + BOOST_TEST_EQ(a[0], 9); + BOOST_TEST_EQ(a[1], 6); + } + + // copy + { + storage_adaptor a; + a.reset(1); + a(0); + decltype(a) b; + b.reset(2); + BOOST_TEST(!(a == b)); + b = a; + BOOST_TEST(a == b); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(b[0], 1); + + decltype(a) c(a); + BOOST_TEST(a == c); + BOOST_TEST_EQ(c.size(), 1); + BOOST_TEST_EQ(c[0], 1); + } + + // move + { + storage_adaptor a; + a.reset(1); + a(0); + decltype(a) b; + BOOST_TEST(!(a == b)); + b = std::move(a); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(b[0], 1); + decltype(a) c(std::move(b)); + BOOST_TEST_EQ(c.size(), 1); + BOOST_TEST_EQ(c[0], 1); + } + + // add + { + storage_adaptor a; + a.reset(2); + a(1); + auto b = a; + b += a; + BOOST_TEST_EQ(b[0], 0); + BOOST_TEST_EQ(b[1], 2); + a += a; + // also test self-add + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 2); + } + + // multiply + { + storage_adaptor a; + a.reset(2); + a(1); + a *= 2; + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 2); + } +} + +template +void mixed_tests() { + // comparison + { + A a, b; a.reset(1); b.reset(1); - vector_storage c, d; + B c, d; c.reset(1); d.reset(2); a(0); @@ -65,69 +165,37 @@ int main() { BOOST_TEST(!(a == d)); } - // multiply + // ctor, copy, move { - vector_storage a; + A a; a.reset(2); - a(0); - a *= 3; - BOOST_TEST_EQ(a[0], 3); - BOOST_TEST_EQ(a[1], 0); - a(1, 2); - BOOST_TEST_EQ(a[0], 3); - BOOST_TEST_EQ(a[1], 2); - a *= 3; - BOOST_TEST_EQ(a[0], 9); - BOOST_TEST_EQ(a[1], 6); + a(1); + B b(a); + B c; + c = a; + BOOST_TEST_EQ(c[0], 0); + BOOST_TEST_EQ(c[1], 1); + B d(std::move(a)); + B e; + e = std::move(d); + BOOST_TEST_EQ(e[0], 0); + BOOST_TEST_EQ(e[1], 1); } +} - // copy - { - vector_storage a; - a.reset(1); - a(0); - decltype(a) b; - b.reset(2); - BOOST_TEST(!(a == b)); - b = a; - BOOST_TEST(a == b); - BOOST_TEST_EQ(b.size(), 1); - BOOST_TEST_EQ(b[0], 1); +int main() { + tests>(); + tests>(); + // tests>(); - decltype(a) c(a); - BOOST_TEST(a == c); - BOOST_TEST_EQ(c.size(), 1); - BOOST_TEST_EQ(c[0], 1); - - vector_storage d; - d.reset(1); - BOOST_TEST(!(a == d)); - d = a; - BOOST_TEST(a == d); - decltype(d) e(a); - BOOST_TEST(a == e); - } - - // move - { - vector_storage a; - a.reset(1); - a(0); - decltype(a) b; - BOOST_TEST(!(a == b)); - b = std::move(a); - BOOST_TEST_EQ(a.size(), 0); - BOOST_TEST_EQ(b.size(), 1); - BOOST_TEST_EQ(b[0], 1); - decltype(a) c(std::move(b)); - BOOST_TEST_EQ(c.size(), 1); - BOOST_TEST_EQ(c[0], 1); - BOOST_TEST_EQ(b.size(), 0); - } + mixed_tests>, + storage_adaptor>>(); + mixed_tests, storage_adaptor>>(); + mixed_tests>, adaptive_storage<>>(); // with weight_counter { - vector_storage> a; + storage_adaptor>> a; a.reset(1); a(0); a(0, 1); diff --git a/test/utility.hpp b/test/utility.hpp deleted file mode 100644 index 577cef5e..00000000 --- a/test/utility.hpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2018 Hans Dembinski -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt -// or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_HISTOGRAM_TEST_UTILITY_HPP -#define BOOST_HISTOGRAM_TEST_UTILITY_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using i0 = boost::mp11::mp_size_t<0>; -using i1 = boost::mp11::mp_size_t<1>; -using i2 = boost::mp11::mp_size_t<2>; -using i3 = boost::mp11::mp_size_t<3>; - -namespace std { -// never add to std, we only do it here to get ADL working :( -template -ostream& operator<<(ostream& os, const vector& v) { - os << "[ "; - for (const auto& x : v) os << x << " "; - os << "]"; - return os; -} - -template -ostream& operator<<(ostream& os, const tuple& t) { - os << "[ "; - ::boost::mp11::tuple_for_each(t, [&os](const auto& x) { os << x << " "; }); - os << "]"; - return os; -} -} // namespace std - -namespace boost { -namespace histogram { -template -auto sum(const Histogram& h) { - return std::accumulate(h.begin(), h.end(), typename Histogram::value_type(0)); -} - -template -std::vector...>> make_axis_vector(Ts&&... ts) { - using T = axis::variant...>; - return std::vector({T(std::forward(ts))...}); -} - -using static_tag = std::false_type; -using dynamic_tag = std::true_type; - -template -auto make(static_tag, Axes&&... axes) - -> decltype(make_histogram(std::forward(axes)...)) { - return make_histogram(std::forward(axes)...); -} - -template -auto make_s(static_tag, S&& s, Axes&&... axes) - -> decltype(make_histogram_with(s, std::forward(axes)...)) { - return make_histogram_with(s, std::forward(axes)...); -} - -template -auto make(dynamic_tag, Axes&&... axes) - -> decltype(make_histogram(make_axis_vector(std::forward(axes)...))) { - return make_histogram(make_axis_vector(std::forward(axes)...)); -} - -template -auto make_s(dynamic_tag, S&& s, Axes&&... axes) - -> decltype(make_histogram_with(s, make_axis_vector(std::forward(axes)...))) { - return make_histogram_with(s, make_axis_vector(std::forward(axes)...)); -} - -using tracing_allocator_db = - std::unordered_map>; - -template -struct tracing_allocator { - using value_type = T; - - tracing_allocator_db* db = nullptr; - - tracing_allocator() noexcept {} - tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {} - template - tracing_allocator(const tracing_allocator& a) noexcept : db(a.db) {} - ~tracing_allocator() noexcept {} - - T* allocate(std::size_t n) { - if (db) { (*db)[typeid(T)].first += n; } - return static_cast(::operator new(n * sizeof(T))); - } - - void deallocate(T* p, std::size_t n) { - if (db) { (*db)[typeid(T)].second += n; } - ::operator delete((void*)p); - } -}; - -template -constexpr bool operator==(const tracing_allocator&, - const tracing_allocator&) noexcept { - return true; -} - -template -constexpr bool operator!=(const tracing_allocator& t, - const tracing_allocator& u) noexcept { - return !operator==(t, u); -} - -template -void test_axis_iterator(const Axis& a, int begin, int end) { - for (auto bin : a) { - BOOST_TEST_EQ(bin.idx(), begin); - BOOST_TEST_EQ(bin, a[begin]); - ++begin; - } - BOOST_TEST_EQ(begin, end); - auto rit = a.rbegin(); - for (; rit != a.rend(); ++rit) { - BOOST_TEST_EQ(rit->idx(), --begin); - BOOST_TEST_EQ(*rit, a[begin]); - } -} - -#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) - -} // namespace histogram -} // namespace boost - -#endif diff --git a/test/utility_allocator.hpp b/test/utility_allocator.hpp new file mode 100644 index 00000000..99b9a08b --- /dev/null +++ b/test/utility_allocator.hpp @@ -0,0 +1,64 @@ +// Copyright 2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_TEST_UTILITY_ALLOCATOR_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_ALLOCATOR_HPP + +#include +#include +#include +#include + +namespace boost { +namespace histogram { + +struct tracing_allocator_db : std::unordered_map> { + template + std::pair& at() { + return this->operator[](&BOOST_CORE_TYPEID(T)); + } +}; + +template +struct tracing_allocator { + using value_type = T; + + tracing_allocator_db* db = nullptr; + + tracing_allocator() noexcept {} + tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {} + template + tracing_allocator(const tracing_allocator& a) noexcept : db(a.db) {} + ~tracing_allocator() noexcept {} + + T* allocate(std::size_t n) { + if (db) { db->at().first += n; } + return static_cast(::operator new(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t n) { + if (db) { db->at().second += n; } + ::operator delete((void*)p); + } +}; + +template +constexpr bool operator==(const tracing_allocator&, + const tracing_allocator&) noexcept { + return true; +} + +template +constexpr bool operator!=(const tracing_allocator& t, + const tracing_allocator& u) noexcept { + return !operator==(t, u); +} + +} // namespace histogram +} // namespace boost + +#endif diff --git a/test/utility_axis.hpp b/test/utility_axis.hpp new file mode 100644 index 00000000..3723208b --- /dev/null +++ b/test/utility_axis.hpp @@ -0,0 +1,35 @@ +// Copyright 2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_TEST_UTILITY_AXIS_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_AXIS_HPP + +#include + +namespace boost { +namespace histogram { + +template +void test_axis_iterator(const Axis& a, int begin, int end) { + for (auto bin : a) { + BOOST_TEST_EQ(bin.idx(), begin); + BOOST_TEST_EQ(bin, a[begin]); + ++begin; + } + BOOST_TEST_EQ(begin, end); + auto rit = a.rbegin(); + for (; rit != a.rend(); ++rit) { + BOOST_TEST_EQ(rit->idx(), --begin); + BOOST_TEST_EQ(*rit, a[begin]); + } +} + +#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) + +} // namespace histogram +} // namespace boost + +#endif diff --git a/test/utility_histogram.hpp b/test/utility_histogram.hpp new file mode 100644 index 00000000..dde13c6d --- /dev/null +++ b/test/utility_histogram.hpp @@ -0,0 +1,57 @@ +// Copyright 2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_TEST_UTILITY_HISTOGRAM_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_HISTOGRAM_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +template +auto sum(const Histogram& h) { + return std::accumulate(h.begin(), h.end(), typename Histogram::value_type(0)); +} + +template +auto make_axis_vector(Ts&&... ts) { + using T = axis::variant...>; + return std::vector({T(std::forward(ts))...}); +} + +using static_tag = std::false_type; +using dynamic_tag = std::true_type; + +template +auto make(static_tag, Axes&&... axes) { + return make_histogram(std::forward(axes)...); +} + +template +auto make_s(static_tag, S&& s, Axes&&... axes) { + return make_histogram_with(s, std::forward(axes)...); +} + +template +auto make(dynamic_tag, Axes&&... axes) { + return make_histogram(make_axis_vector(std::forward(axes)...)); +} + +template +auto make_s(dynamic_tag, S&& s, Axes&&... axes) { + return make_histogram_with(s, make_axis_vector(std::forward(axes)...)); +} + +} // namespace histogram +} // namespace boost + +#endif diff --git a/test/utility_meta.hpp b/test/utility_meta.hpp new file mode 100644 index 00000000..ffbf8653 --- /dev/null +++ b/test/utility_meta.hpp @@ -0,0 +1,40 @@ +// Copyright 2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_TEST_UTILITY_META_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_META_HPP + +#include +#include +#include +#include +#include + +using i0 = boost::mp11::mp_size_t<0>; +using i1 = boost::mp11::mp_size_t<1>; +using i2 = boost::mp11::mp_size_t<2>; +using i3 = boost::mp11::mp_size_t<3>; + +namespace std { +// never add to std, we only do it here to get ADL working :( +template +ostream& operator<<(ostream& os, const vector& v) { + os << "[ "; + for (const auto& x : v) os << x << " "; + os << "]"; + return os; +} + +template +ostream& operator<<(ostream& os, const tuple& t) { + os << "[ "; + ::boost::mp11::tuple_for_each(t, [&os](const auto& x) { os << x << " "; }); + os << "]"; + return os; +} +} // namespace std + +#endif diff --git a/test/utility_test.cpp b/test/utility_test.cpp index b8fffaac..9c16c1f6 100644 --- a/test/utility_test.cpp +++ b/test/utility_test.cpp @@ -4,11 +4,12 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "utility.hpp" #include #include #include #include +#include "utility_allocator.hpp" +#include "utility_meta.hpp" using namespace boost::histogram; @@ -40,9 +41,9 @@ int main() { auto p2 = b.allocate(3); b.deallocate(p2, 3); BOOST_TEST_EQ(db.size(), 2); - BOOST_TEST_EQ(db[typeid(char)].first, 2); - BOOST_TEST_EQ(db[typeid(char)].second, 2); - BOOST_TEST_EQ(db[typeid(int)].first, 3); - BOOST_TEST_EQ(db[typeid(int)].second, 3); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(char)].first, 2); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(char)].second, 2); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(int)].first, 3); + BOOST_TEST_EQ(db[&BOOST_CORE_TYPEID(int)].second, 3); } }