mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-09 23:04:07 +00:00
Replace detail::compressed_pair with boost::core::empty_value
This commit is contained in:
parent
5e24146062
commit
ebabd550a0
@ -10,11 +10,8 @@
|
||||
#include <algorithm>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_default.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
@ -44,9 +41,10 @@ namespace axis {
|
||||
and `overflow` are mutually exclusive.
|
||||
*/
|
||||
template <class Value, class MetaData, class Options, class Allocator>
|
||||
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>> {
|
||||
class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>,
|
||||
public metadata_base<MetaData> {
|
||||
using value_type = Value;
|
||||
using metadata_type = detail::replace_default<MetaData, std::string>;
|
||||
using metadata_type = typename metadata_base<MetaData>::metadata_type;
|
||||
using options_type = detail::replace_default<Options, option::overflow_t>;
|
||||
using allocator_type = Allocator;
|
||||
using vector_type = std::vector<value_type, allocator_type>;
|
||||
@ -60,23 +58,8 @@ class category : public iterator_mixin<category<Value, MetaData, Options, Alloca
|
||||
"growing category axis cannot have entries in overflow bin");
|
||||
|
||||
public:
|
||||
explicit category(allocator_type alloc = {}) : vec_meta_(vector_type(alloc)) {}
|
||||
category(const category&) = default;
|
||||
category& operator=(const category&) = default;
|
||||
category(category&& o) noexcept : vec_meta_(std::move(o.vec_meta_)) {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"");
|
||||
}
|
||||
category& operator=(category&& o) noexcept {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_assignable<metadata_type>::value,
|
||||
"");
|
||||
vec_meta_ = std::move(o.vec_meta_);
|
||||
return *this;
|
||||
}
|
||||
constexpr category() = default;
|
||||
explicit category(allocator_type alloc) : vec_(alloc) {}
|
||||
|
||||
/** Construct from iterator range of unique values.
|
||||
*
|
||||
@ -87,7 +70,7 @@ public:
|
||||
*/
|
||||
template <class It, class = detail::requires_iterator<It>>
|
||||
category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
|
||||
: vec_meta_(vector_type(begin, end, alloc), std::move(meta)) {
|
||||
: metadata_base<MetaData>(std::move(meta)), vec_(begin, end, alloc) {
|
||||
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
|
||||
}
|
||||
|
||||
@ -115,8 +98,8 @@ public:
|
||||
|
||||
/// Return index for value argument.
|
||||
index_type index(const value_type& x) const noexcept {
|
||||
const auto beg = vec_meta_.first().begin();
|
||||
const auto end = vec_meta_.first().end();
|
||||
const auto beg = vec_.begin();
|
||||
const auto end = vec_.end();
|
||||
return static_cast<index_type>(std::distance(beg, std::find(beg, end, x)));
|
||||
}
|
||||
|
||||
@ -124,7 +107,7 @@ public:
|
||||
auto update(const value_type& x) {
|
||||
const auto i = index(x);
|
||||
if (i < size()) return std::make_pair(i, 0);
|
||||
vec_meta_.first().emplace_back(x);
|
||||
vec_.emplace_back(x);
|
||||
return std::make_pair(i, -1);
|
||||
}
|
||||
|
||||
@ -135,16 +118,14 @@ public:
|
||||
const value_type&> {
|
||||
if (idx < 0 || idx >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range"));
|
||||
return vec_meta_.first()[idx];
|
||||
return vec_[idx];
|
||||
}
|
||||
|
||||
/// Return value for index argument.
|
||||
decltype(auto) bin(index_type idx) const noexcept { return value(idx); }
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept {
|
||||
return static_cast<index_type>(vec_meta_.first().size());
|
||||
}
|
||||
index_type size() const noexcept { return static_cast<index_type>(vec_.size()); }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
@ -154,18 +135,12 @@ public:
|
||||
return options() & (option::overflow | option::growth);
|
||||
}
|
||||
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return vec_meta_.second(); }
|
||||
|
||||
/// Returns reference to const metadata.
|
||||
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
bool operator==(const category<V, M, O, A>& o) const noexcept {
|
||||
const auto& a = vec_meta_.first();
|
||||
const auto& b = o.vec_meta_.first();
|
||||
const auto& a = vec_;
|
||||
const auto& b = o.vec_;
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
|
||||
detail::relaxed_equal(metadata(), o.metadata());
|
||||
metadata_base<MetaData>::operator==(o);
|
||||
}
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
@ -173,16 +148,16 @@ public:
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
|
||||
auto get_allocator() const { return vec_.get_allocator(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("seq", vec_meta_.first());
|
||||
ar& make_nvp("meta", vec_meta_.second());
|
||||
ar& make_nvp("seq", vec_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
detail::compressed_pair<vector_type, metadata_type> vec_meta_;
|
||||
vector_type vec_;
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
friend class category;
|
||||
|
@ -9,11 +9,10 @@
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/limits.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_default.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
@ -39,7 +38,8 @@ namespace axis {
|
||||
@tparam Options see boost::histogram::axis::option (all values allowed).
|
||||
*/
|
||||
template <class Value, class MetaData, class Options>
|
||||
class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
|
||||
class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
|
||||
public metadata_base<MetaData> {
|
||||
static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
|
||||
"integer axis requires floating point or integral type");
|
||||
|
||||
@ -47,7 +47,7 @@ class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
|
||||
using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
|
||||
index_type, real_index_type>;
|
||||
|
||||
using metadata_type = detail::replace_default<MetaData, std::string>;
|
||||
using metadata_type = typename metadata_base<MetaData>::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
|
||||
@ -66,23 +66,6 @@ class integer : public iterator_mixin<integer<Value, MetaData, Options>> {
|
||||
|
||||
public:
|
||||
constexpr integer() = default;
|
||||
integer(const integer&) = default;
|
||||
integer& operator=(const integer&) = default;
|
||||
integer(integer&& o) noexcept : size_meta_(std::move(o.size_meta_)), min_(o.min_) {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"");
|
||||
}
|
||||
integer& operator=(integer&& o) noexcept {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_assignable<metadata_type>::value,
|
||||
"");
|
||||
size_meta_ = std::move(o.size_meta_);
|
||||
min_ = o.min_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Construct over semi-open integer interval [start, stop).
|
||||
*
|
||||
@ -91,7 +74,9 @@ public:
|
||||
* \param meta description of the axis.
|
||||
*/
|
||||
integer(value_type start, value_type stop, metadata_type meta = {})
|
||||
: size_meta_(static_cast<index_type>(stop - start), std::move(meta)), min_(start) {
|
||||
: metadata_base<MetaData>(std::move(meta))
|
||||
, size_(static_cast<index_type>(stop - start))
|
||||
, min_(start) {
|
||||
if (stop <= start) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
|
||||
}
|
||||
|
||||
@ -117,21 +102,20 @@ public:
|
||||
const auto k = static_cast<axis::index_type>(i);
|
||||
if (k < size()) return std::make_pair(k, 0);
|
||||
const auto n = k - size() + 1;
|
||||
size_meta_.first() += n;
|
||||
size_ += n;
|
||||
return std::make_pair(k, -n);
|
||||
}
|
||||
const auto k = static_cast<axis::index_type>(
|
||||
detail::static_if<std::is_floating_point<value_type>>(
|
||||
[](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
|
||||
min_ += k;
|
||||
size_meta_.first() -= k;
|
||||
size_ -= k;
|
||||
return std::make_pair(0, -k);
|
||||
};
|
||||
|
||||
return detail::static_if<std::is_floating_point<value_type>>(
|
||||
[this, impl](auto x) {
|
||||
if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
|
||||
// this->size() is workaround for gcc-5 bug
|
||||
return std::make_pair(x < 0 ? -1 : this->size(), 0);
|
||||
},
|
||||
impl, x);
|
||||
@ -155,7 +139,7 @@ public:
|
||||
}
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept { return size_meta_.first(); }
|
||||
index_type size() const noexcept { return size_; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
@ -167,16 +151,9 @@ public:
|
||||
(options() & (option::growth | option::circular)));
|
||||
}
|
||||
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return size_meta_.second(); }
|
||||
|
||||
/// Returns reference to const metadata.
|
||||
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
|
||||
|
||||
template <class V, class M, class O>
|
||||
bool operator==(const integer<V, M, O>& o) const noexcept {
|
||||
return size() == o.size() && detail::relaxed_equal(metadata(), o.metadata()) &&
|
||||
min_ == o.min_;
|
||||
return size() == o.size() && min_ == o.min_ && metadata_base<MetaData>::operator==(o);
|
||||
}
|
||||
|
||||
template <class V, class M, class O>
|
||||
@ -186,8 +163,8 @@ public:
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("size", size_meta_.first());
|
||||
ar& make_nvp("meta", size_meta_.second());
|
||||
ar& make_nvp("size", size_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
ar& make_nvp("min", min_);
|
||||
}
|
||||
|
||||
@ -213,7 +190,7 @@ private:
|
||||
return size();
|
||||
}
|
||||
|
||||
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
|
||||
index_type size_{0};
|
||||
value_type min_{0};
|
||||
|
||||
template <class V, class M, class O>
|
||||
|
67
include/boost/histogram/axis/metadata_base.hpp
Normal file
67
include/boost/histogram/axis/metadata_base.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2019 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_AXIS_METADATA_BASE_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_METADATA_BASE_HPP
|
||||
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_default.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
/// Meta data holder with space optimization for empty meta data types.
|
||||
template <class Metadata,
|
||||
class DetailMetadata = detail::replace_default<Metadata, std::string>>
|
||||
class metadata_base : empty_value<DetailMetadata> {
|
||||
using base_t = empty_value<DetailMetadata>;
|
||||
|
||||
protected:
|
||||
using metadata_type = DetailMetadata;
|
||||
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"metadata must be nothrow move constructible");
|
||||
|
||||
metadata_base() = default;
|
||||
metadata_base(const metadata_base&) = default;
|
||||
metadata_base& operator=(const metadata_base&) = default;
|
||||
|
||||
// make noexcept because std::string is nothrow move constructible only in C++17
|
||||
metadata_base(metadata_base&& o) noexcept : base_t(std::move(o)) {}
|
||||
metadata_base(metadata_type&& o) noexcept : base_t(empty_init_t{}, std::move(o)) {}
|
||||
// make noexcept because std::string is nothrow move constructible only in C++17
|
||||
metadata_base& operator=(metadata_base&& o) noexcept {
|
||||
base_t::operator=(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return base_t::get(); }
|
||||
|
||||
/// Returns reference to const metadata.
|
||||
const metadata_type& metadata() const noexcept { return base_t::get(); }
|
||||
|
||||
bool operator==(const metadata_base& o) const noexcept {
|
||||
return detail::relaxed_equal(metadata(), o.metadata());
|
||||
}
|
||||
|
||||
bool operator!=(const metadata_base& o) const noexcept {
|
||||
return operator==(o.metadata());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -11,8 +11,8 @@
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_default.hpp>
|
||||
@ -177,13 +177,17 @@ step_type<T> step(T t) {
|
||||
*/
|
||||
template <class Value, class Transform, class MetaData, class Options>
|
||||
class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
|
||||
protected detail::replace_default<Transform, transform::id> {
|
||||
protected detail::replace_default<Transform, transform::id>,
|
||||
public metadata_base<MetaData> {
|
||||
using value_type = Value;
|
||||
using transform_type = detail::replace_default<Transform, transform::id>;
|
||||
using metadata_type = detail::replace_default<MetaData, std::string>;
|
||||
using metadata_type = typename metadata_base<MetaData>::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<transform_type>::value, "");
|
||||
static_assert(std::is_nothrow_move_assignable<transform_type>::value, "");
|
||||
|
||||
using unit_type = detail::get_unit_type<value_type>;
|
||||
using internal_value_type = detail::get_scale_type<value_type>;
|
||||
|
||||
@ -197,31 +201,6 @@ class regular : public iterator_mixin<regular<Value, Transform, MetaData, Option
|
||||
|
||||
public:
|
||||
constexpr regular() = default;
|
||||
regular(const regular&) = default;
|
||||
regular& operator=(const regular&) = default;
|
||||
regular(regular&& o) noexcept
|
||||
: transform_type(std::move(o))
|
||||
, size_meta_(std::move(o.size_meta_))
|
||||
, min_(o.min_)
|
||||
, delta_(o.delta_) {
|
||||
static_assert(std::is_nothrow_move_constructible<transform_type>::value, "");
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"");
|
||||
}
|
||||
regular& operator=(regular&& o) noexcept {
|
||||
static_assert(std::is_nothrow_move_assignable<transform_type>::value, "");
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_assignable<metadata_type>::value,
|
||||
"");
|
||||
transform_type::operator=(std::move(o));
|
||||
size_meta_ = std::move(o.size_meta_);
|
||||
min_ = o.min_;
|
||||
delta_ = o.delta_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Construct n bins over real transformed range [start, stop).
|
||||
*
|
||||
@ -234,7 +213,8 @@ public:
|
||||
regular(transform_type trans, unsigned n, value_type start, value_type stop,
|
||||
metadata_type meta = {})
|
||||
: transform_type(std::move(trans))
|
||||
, size_meta_(static_cast<index_type>(n), std::move(meta))
|
||||
, metadata_base<MetaData>(std::move(meta))
|
||||
, size_(static_cast<index_type>(n))
|
||||
, min_(this->forward(detail::get_scale(start)))
|
||||
, delta_(this->forward(detail::get_scale(stop)) - min_) {
|
||||
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
|
||||
@ -338,7 +318,7 @@ public:
|
||||
const auto i = static_cast<axis::index_type>(std::floor(z * size()));
|
||||
min_ += i * (delta_ / size());
|
||||
delta_ = stop - min_;
|
||||
size_meta_.first() -= i;
|
||||
size_ -= i;
|
||||
return std::make_pair(0, -i);
|
||||
}
|
||||
// z is -infinity
|
||||
@ -350,7 +330,7 @@ public:
|
||||
const auto n = i - size() + 1;
|
||||
delta_ /= size();
|
||||
delta_ *= size() + n;
|
||||
size_meta_.first() += n;
|
||||
size_ += n;
|
||||
return std::make_pair(i, -n);
|
||||
}
|
||||
// z either infinite or NaN
|
||||
@ -376,22 +356,15 @@ public:
|
||||
}
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept { return size_meta_.first(); }
|
||||
index_type size() const noexcept { return size_; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return size_meta_.second(); }
|
||||
|
||||
/// Returns reference to const metadata.
|
||||
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
|
||||
|
||||
template <class V, class T, class M, class O>
|
||||
bool operator==(const regular<V, T, M, O>& o) const noexcept {
|
||||
return detail::relaxed_equal(transform(), o.transform()) && size() == o.size() &&
|
||||
detail::relaxed_equal(metadata(), o.metadata()) && min_ == o.min_ &&
|
||||
delta_ == o.delta_;
|
||||
min_ == o.min_ && delta_ == o.delta_ && metadata_base<MetaData>::operator==(o);
|
||||
}
|
||||
template <class V, class T, class M, class O>
|
||||
bool operator!=(const regular<V, T, M, O>& o) const noexcept {
|
||||
@ -401,14 +374,14 @@ public:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("transform", static_cast<transform_type&>(*this));
|
||||
ar& make_nvp("size", size_meta_.first());
|
||||
ar& make_nvp("meta", size_meta_.second());
|
||||
ar& make_nvp("size", size_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
ar& make_nvp("min", min_);
|
||||
ar& make_nvp("delta", delta_);
|
||||
}
|
||||
|
||||
private:
|
||||
detail::compressed_pair<index_type, metadata_type> size_meta_{0};
|
||||
index_type size_{0};
|
||||
internal_value_type min_{0}, delta_{1};
|
||||
|
||||
template <class V, class T, class M, class O>
|
||||
|
@ -12,12 +12,11 @@
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/metadata_base.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/convert_integer.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/limits.hpp>
|
||||
#include <boost/histogram/detail/relaxed_equal.hpp>
|
||||
#include <boost/histogram/detail/replace_default.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
@ -46,13 +45,14 @@ namespace axis {
|
||||
@tparam Allocator allocator to use for dynamic memory management.
|
||||
*/
|
||||
template <class Value, class MetaData, class Options, class Allocator>
|
||||
class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>> {
|
||||
class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>>,
|
||||
public metadata_base<MetaData> {
|
||||
using value_type = Value;
|
||||
using metadata_type = detail::replace_default<MetaData, std::string>;
|
||||
using metadata_type = typename metadata_base<MetaData>::metadata_type;
|
||||
using options_type =
|
||||
detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
|
||||
using allocator_type = Allocator;
|
||||
using vec_type = std::vector<Value, allocator_type>;
|
||||
using vector_type = std::vector<Value, allocator_type>;
|
||||
|
||||
static_assert(
|
||||
std::is_floating_point<value_type>::value,
|
||||
@ -65,23 +65,8 @@ class variable : public iterator_mixin<variable<Value, MetaData, Options, Alloca
|
||||
"circular and growth options are mutually exclusive");
|
||||
|
||||
public:
|
||||
explicit variable(allocator_type alloc = {}) : vec_meta_(vec_type{alloc}) {}
|
||||
variable(const variable&) = default;
|
||||
variable& operator=(const variable&) = default;
|
||||
variable(variable&& o) noexcept : vec_meta_(std::move(o.vec_meta_)) {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_constructible<metadata_type>::value,
|
||||
"");
|
||||
}
|
||||
variable& operator=(variable&& o) noexcept {
|
||||
// std::string explicitly guarantees nothrow only in C++17
|
||||
static_assert(std::is_same<metadata_type, std::string>::value ||
|
||||
std::is_nothrow_move_assignable<metadata_type>::value,
|
||||
"");
|
||||
vec_meta_ = std::move(o.vec_meta_);
|
||||
return *this;
|
||||
}
|
||||
constexpr variable() = default;
|
||||
explicit variable(allocator_type alloc) : vec_(alloc) {}
|
||||
|
||||
/** Construct from iterator range of bin edges.
|
||||
*
|
||||
@ -92,18 +77,17 @@ public:
|
||||
*/
|
||||
template <class It, class = detail::requires_iterator<It>>
|
||||
variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
|
||||
: vec_meta_(vec_type(std::move(alloc)), std::move(meta)) {
|
||||
: metadata_base<MetaData>(std::move(meta)), vec_(std::move(alloc)) {
|
||||
if (std::distance(begin, end) <= 1)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
|
||||
|
||||
auto& v = vec_meta_.first();
|
||||
v.reserve(std::distance(begin, end));
|
||||
v.emplace_back(*begin++);
|
||||
vec_.reserve(std::distance(begin, end));
|
||||
vec_.emplace_back(*begin++);
|
||||
while (begin != end) {
|
||||
if (*begin <= v.back())
|
||||
if (*begin <= vec_.back())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("input sequence must be strictly ascending"));
|
||||
v.emplace_back(*begin++);
|
||||
vec_.emplace_back(*begin++);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,43 +115,40 @@ public:
|
||||
|
||||
/// Constructor used by algorithm::reduce to shrink and rebin (not for users).
|
||||
variable(const variable& src, index_type begin, index_type end, unsigned merge)
|
||||
: vec_meta_(vec_type(src.get_allocator()), src.metadata()) {
|
||||
: metadata_base<MetaData>(src), vec_(src.get_allocator()) {
|
||||
BOOST_ASSERT((end - begin) % merge == 0);
|
||||
if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
|
||||
auto& vec = vec_meta_.first();
|
||||
vec.reserve((end - begin) / merge);
|
||||
const auto beg = src.vec_meta_.first().begin();
|
||||
for (index_type i = begin; i <= end; i += merge) vec.emplace_back(*(beg + i));
|
||||
vec_.reserve((end - begin) / merge);
|
||||
const auto beg = src.vec_.begin();
|
||||
for (index_type i = begin; i <= end; i += merge) vec_.emplace_back(*(beg + i));
|
||||
}
|
||||
|
||||
/// Return index for value argument.
|
||||
index_type index(value_type x) const noexcept {
|
||||
const auto& v = vec_meta_.first();
|
||||
if (options_type::test(option::circular)) {
|
||||
const auto a = v[0];
|
||||
const auto b = v[size()];
|
||||
const auto a = vec_[0];
|
||||
const auto b = vec_[size()];
|
||||
x -= std::floor((x - a) / (b - a)) * (b - a);
|
||||
}
|
||||
return static_cast<index_type>(std::upper_bound(v.begin(), v.end(), x) - v.begin() -
|
||||
1);
|
||||
return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) -
|
||||
vec_.begin() - 1);
|
||||
}
|
||||
|
||||
auto update(value_type x) noexcept {
|
||||
const auto i = index(x);
|
||||
if (std::isfinite(x)) {
|
||||
auto& vec = vec_meta_.first();
|
||||
if (0 <= i) {
|
||||
if (i < size()) return std::make_pair(i, 0);
|
||||
const auto d = value(size()) - value(size() - 0.5);
|
||||
x = std::nextafter(x, (std::numeric_limits<value_type>::max)());
|
||||
x = (std::max)(x, vec.back() + d);
|
||||
vec.push_back(x);
|
||||
x = (std::max)(x, vec_.back() + d);
|
||||
vec_.push_back(x);
|
||||
return std::make_pair(i, -1);
|
||||
}
|
||||
const auto d = value(0.5) - value(0);
|
||||
x = (std::min)(x, value(0) - d);
|
||||
vec.insert(vec.begin(), x);
|
||||
vec_.insert(vec_.begin(), x);
|
||||
return std::make_pair(0, -i);
|
||||
}
|
||||
return std::make_pair(x < 0 ? -1 : size(), 0);
|
||||
@ -175,47 +156,38 @@ public:
|
||||
|
||||
/// Return value for fractional index argument.
|
||||
value_type value(real_index_type i) const noexcept {
|
||||
const auto& v = vec_meta_.first();
|
||||
if (options_type::test(option::circular)) {
|
||||
auto shift = std::floor(i / size());
|
||||
i -= shift * size();
|
||||
double z;
|
||||
const auto k = static_cast<index_type>(std::modf(i, &z));
|
||||
const auto a = v[0];
|
||||
const auto b = v[size()];
|
||||
return (1.0 - z) * v[k] + z * v[k + 1] + shift * (b - a);
|
||||
const auto a = vec_[0];
|
||||
const auto b = vec_[size()];
|
||||
return (1.0 - z) * vec_[k] + z * vec_[k + 1] + shift * (b - a);
|
||||
}
|
||||
if (i < 0) return detail::lowest<value_type>();
|
||||
if (i == size()) return v.back();
|
||||
if (i == size()) return vec_.back();
|
||||
if (i > size()) return detail::highest<value_type>();
|
||||
const auto k = static_cast<index_type>(i); // precond: i >= 0
|
||||
const real_index_type z = i - k;
|
||||
return (1.0 - z) * v[k] + z * v[k + 1];
|
||||
return (1.0 - z) * vec_[k] + z * vec_[k + 1];
|
||||
}
|
||||
|
||||
/// Return bin for index argument.
|
||||
auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }
|
||||
|
||||
/// Returns the number of bins, without over- or underflow.
|
||||
index_type size() const noexcept {
|
||||
return static_cast<index_type>(vec_meta_.first().size()) - 1;
|
||||
}
|
||||
index_type size() const noexcept { return static_cast<index_type>(vec_.size()) - 1; }
|
||||
|
||||
/// Returns the options.
|
||||
static constexpr unsigned options() noexcept { return options_type::value; }
|
||||
|
||||
/// Returns reference to metadata.
|
||||
metadata_type& metadata() noexcept { return vec_meta_.second(); }
|
||||
|
||||
/// Returns reference to const metadata.
|
||||
const metadata_type& metadata() const noexcept { return vec_meta_.second(); }
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
bool operator==(const variable<V, M, O, A>& o) const noexcept {
|
||||
const auto& a = vec_meta_.first();
|
||||
const auto& b = o.vec_meta_.first();
|
||||
const auto& a = vec_;
|
||||
const auto& b = o.vec_;
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
|
||||
detail::relaxed_equal(metadata(), o.metadata());
|
||||
metadata_base<MetaData>::operator==(o);
|
||||
}
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
@ -224,16 +196,16 @@ public:
|
||||
}
|
||||
|
||||
/// Return allocator instance.
|
||||
auto get_allocator() const { return vec_meta_.first().get_allocator(); }
|
||||
auto get_allocator() const { return vec_.get_allocator(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("seq", vec_meta_.first());
|
||||
ar& make_nvp("meta", vec_meta_.second());
|
||||
ar& make_nvp("seq", vec_);
|
||||
ar& make_nvp("meta", this->metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
detail::compressed_pair<vec_type, metadata_type> vec_meta_;
|
||||
vector_type vec_;
|
||||
|
||||
template <class V, class M, class O, class A>
|
||||
friend class variable;
|
||||
|
@ -1,94 +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_DETAIL_COMPRESSED_PAIR_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T1, class T2, bool B>
|
||||
class compressed_pair_impl;
|
||||
|
||||
// normal implementation
|
||||
template <class T1, class T2>
|
||||
class compressed_pair_impl<T1, T2, false> {
|
||||
public:
|
||||
using first_type = T1;
|
||||
using second_type = T2;
|
||||
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
compressed_pair_impl(first_type&& x, second_type&& y)
|
||||
: first_(std::move(x)), second_(std::move(y)) {}
|
||||
|
||||
compressed_pair_impl(const first_type& x, const second_type& y)
|
||||
: first_(x), second_(y) {}
|
||||
|
||||
compressed_pair_impl(first_type&& x) : first_(std::move(x)) {}
|
||||
compressed_pair_impl(const first_type& x) : first_(x) {}
|
||||
|
||||
first_type& first() noexcept { return first_; }
|
||||
second_type& second() noexcept { return second_; }
|
||||
const first_type& first() const noexcept { return first_; }
|
||||
const second_type& second() const noexcept { return second_; }
|
||||
|
||||
private:
|
||||
first_type first_;
|
||||
second_type second_;
|
||||
};
|
||||
|
||||
// compressed implementation, T2 consumes no space
|
||||
template <class T1, class T2>
|
||||
class compressed_pair_impl<T1, T2, true> : protected T2 {
|
||||
public:
|
||||
using first_type = T1;
|
||||
using second_type = T2;
|
||||
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
compressed_pair_impl(first_type&& x, second_type&& y)
|
||||
: T2(std::move(y)), first_(std::move(x)) {}
|
||||
|
||||
compressed_pair_impl(const first_type& x, const second_type& y) : T2(y), first_(x) {}
|
||||
|
||||
compressed_pair_impl(first_type&& x) : first_(std::move(x)) {}
|
||||
compressed_pair_impl(const first_type& x) : first_(x) {}
|
||||
|
||||
first_type& first() noexcept { return first_; }
|
||||
second_type& second() noexcept { return static_cast<second_type&>(*this); }
|
||||
const first_type& first() const noexcept { return first_; }
|
||||
const second_type& second() const noexcept {
|
||||
return static_cast<const second_type&>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
first_type first_;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
using compressed_pair =
|
||||
compressed_pair_impl<T1, T2, (!std::is_final<T2>::value && std::is_empty<T2>::value)>;
|
||||
|
||||
template <class T, class U>
|
||||
void swap(compressed_pair<T, U>& a, compressed_pair<T, U>& b) noexcept(
|
||||
std::is_nothrow_move_constructible<T>::value&& std::is_nothrow_move_assignable<
|
||||
T>::value&& std::is_nothrow_move_constructible<U>::value&&
|
||||
std::is_nothrow_move_assignable<U>::value) {
|
||||
using std::swap;
|
||||
swap(a.first(), b.first());
|
||||
swap(a.second(), b.second());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
41
include/boost/histogram/detail/mutex_base.hpp
Normal file
41
include/boost/histogram/detail/mutex_base.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2019 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_DETAIL_NOOP_MUTEX_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_NOOP_MUTEX_HPP
|
||||
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/mp11/utility.hpp> // mp_if
|
||||
#include <mutex>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
struct null_mutex {
|
||||
bool try_lock() noexcept { return true; }
|
||||
void lock() noexcept {}
|
||||
void unlock() noexcept {}
|
||||
};
|
||||
|
||||
template <class Axes, class Storage,
|
||||
class DetailMutex = mp11::mp_if_c<(Storage::has_threading_support &&
|
||||
detail::has_growing_axis<Axes>::value),
|
||||
std::mutex, detail::null_mutex>>
|
||||
struct mutex_base : empty_value<DetailMutex> {
|
||||
mutex_base() = default;
|
||||
// do not copy or move mutex
|
||||
mutex_base(const mutex_base&) : empty_value<DetailMutex>() {}
|
||||
// do not copy or move mutex
|
||||
mutex_base& operator=(const mutex_base&) { return *this; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2019 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_DETAIL_NOOP_MUTEX_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_NOOP_MUTEX_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
struct noop_mutex {
|
||||
bool try_lock() noexcept { return true; }
|
||||
void lock() noexcept {}
|
||||
void unlock() noexcept {}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -10,12 +10,10 @@
|
||||
#include <boost/histogram/detail/at.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/common_type.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/fill.hpp>
|
||||
#include <boost/histogram/detail/fill_n.hpp>
|
||||
#include <boost/histogram/detail/mutex_base.hpp>
|
||||
#include <boost/histogram/detail/non_member_container_access.hpp>
|
||||
#include <boost/histogram/detail/noop_mutex.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
@ -50,7 +48,9 @@ namespace histogram {
|
||||
@tparam Storage class that implements the storage interface
|
||||
*/
|
||||
template <class Axes, class Storage>
|
||||
class histogram {
|
||||
class histogram : detail::mutex_base<Axes, Storage> {
|
||||
using mutex_base_t = typename detail::mutex_base<Axes, Storage>;
|
||||
|
||||
public:
|
||||
static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
|
||||
static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
|
||||
@ -65,40 +65,10 @@ public:
|
||||
using const_iterator = typename storage_type::const_iterator;
|
||||
|
||||
histogram() = default;
|
||||
histogram(const histogram& rhs)
|
||||
: axes_(rhs.axes_)
|
||||
, storage_and_mutex_(rhs.storage_and_mutex_.first())
|
||||
, offset_(rhs.offset_) {
|
||||
// no checks for too large axes needed
|
||||
}
|
||||
histogram(histogram&& rhs)
|
||||
: axes_(std::move(rhs.axes_))
|
||||
, storage_and_mutex_(std::move(rhs.storage_and_mutex_.first()))
|
||||
, offset_(std::exchange(rhs.offset_, 0)) {
|
||||
// no checks for too large axes needed
|
||||
}
|
||||
histogram& operator=(histogram&& rhs) {
|
||||
// no checks for too large axes needed
|
||||
if (this != &rhs) {
|
||||
axes_ = std::move(rhs.axes_);
|
||||
storage_and_mutex_.first() = std::move(rhs.storage_and_mutex_.first());
|
||||
offset_ = rhs.offset_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
histogram& operator=(const histogram& rhs) {
|
||||
// no checks for too large axes needed
|
||||
if (this != &rhs) {
|
||||
axes_ = rhs.axes_;
|
||||
storage_and_mutex_.first() = rhs.storage_and_mutex_.first();
|
||||
offset_ = rhs.offset_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class A, class S>
|
||||
explicit histogram(histogram<A, S>&& rhs)
|
||||
: storage_and_mutex_(std::move(unsafe_access::storage(rhs)))
|
||||
: storage_(std::move(unsafe_access::storage(rhs)))
|
||||
, offset_(unsafe_access::offset(rhs)) {
|
||||
detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
@ -106,8 +76,7 @@ public:
|
||||
|
||||
template <class A, class S>
|
||||
explicit histogram(const histogram<A, S>& rhs)
|
||||
: storage_and_mutex_(unsafe_access::storage(rhs))
|
||||
, offset_(unsafe_access::offset(rhs)) {
|
||||
: storage_(unsafe_access::storage(rhs)), offset_(unsafe_access::offset(rhs)) {
|
||||
detail::axes_assign(axes_, unsafe_access::axes(rhs));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
}
|
||||
@ -116,7 +85,7 @@ public:
|
||||
histogram& operator=(histogram<A, S>&& rhs) {
|
||||
detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_and_mutex_.first() = std::move(unsafe_access::storage(rhs));
|
||||
storage_ = std::move(unsafe_access::storage(rhs));
|
||||
offset_ = unsafe_access::offset(rhs);
|
||||
return *this;
|
||||
}
|
||||
@ -125,7 +94,7 @@ public:
|
||||
histogram& operator=(const histogram<A, S>& rhs) {
|
||||
detail::axes_assign(axes_, unsafe_access::axes(rhs));
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_and_mutex_.first() = unsafe_access::storage(rhs);
|
||||
storage_ = unsafe_access::storage(rhs);
|
||||
offset_ = unsafe_access::offset(rhs);
|
||||
return *this;
|
||||
}
|
||||
@ -133,10 +102,10 @@ public:
|
||||
template <class A, class S>
|
||||
histogram(A&& a, S&& s)
|
||||
: axes_(std::forward<A>(a))
|
||||
, storage_and_mutex_(std::forward<S>(s))
|
||||
, storage_(std::forward<S>(s))
|
||||
, offset_(detail::offset(axes_)) {
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
storage_and_mutex_.first().reset(detail::bincount(axes_));
|
||||
storage_.reset(detail::bincount(axes_));
|
||||
}
|
||||
|
||||
template <class A, class = detail::requires_axes<A>>
|
||||
@ -146,10 +115,10 @@ public:
|
||||
constexpr unsigned rank() const noexcept { return detail::axes_rank(axes_); }
|
||||
|
||||
/// Total number of bins (including underflow/overflow).
|
||||
std::size_t size() const noexcept { return storage_and_mutex_.first().size(); }
|
||||
std::size_t size() const noexcept { return storage_.size(); }
|
||||
|
||||
/// Reset all bins to default initialized values.
|
||||
void reset() { storage_and_mutex_.first().reset(size()); }
|
||||
void reset() { storage_.reset(size()); }
|
||||
|
||||
/// Get N-th axis using a compile-time number.
|
||||
/// This version is more efficient than the one accepting a run-time number.
|
||||
@ -207,8 +176,8 @@ public:
|
||||
/// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
|
||||
template <class... Ts>
|
||||
iterator operator()(const std::tuple<Ts...>& args) {
|
||||
std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
|
||||
return detail::fill(offset_, storage_and_mutex_.first(), axes_, args);
|
||||
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
|
||||
return detail::fill(offset_, storage_, axes_, args);
|
||||
}
|
||||
|
||||
/** Fill histogram with several values at once.
|
||||
@ -228,8 +197,8 @@ public:
|
||||
*/
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args) {
|
||||
std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
|
||||
detail::fill_n(offset_, storage_and_mutex_.first(), axes_, detail::make_span(args));
|
||||
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
|
||||
detail::fill_n(offset_, storage_, axes_, detail::make_span(args));
|
||||
}
|
||||
|
||||
/** Fill histogram with several values and weights at once.
|
||||
@ -239,8 +208,8 @@ public:
|
||||
*/
|
||||
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const weight_type<T>& weights) {
|
||||
std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
|
||||
detail::fill_n(offset_, storage_and_mutex_.first(), axes_, detail::make_span(args),
|
||||
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
|
||||
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
|
||||
detail::to_ptr_size(weights.value));
|
||||
}
|
||||
|
||||
@ -261,11 +230,11 @@ public:
|
||||
*/
|
||||
template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const sample_type<T>& samples) {
|
||||
std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
|
||||
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... sargs) {
|
||||
detail::fill_n(offset_, storage_and_mutex_.first(), axes_,
|
||||
detail::make_span(args), detail::to_ptr_size(sargs)...);
|
||||
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
|
||||
detail::to_ptr_size(sargs)...);
|
||||
},
|
||||
samples.value);
|
||||
}
|
||||
@ -283,11 +252,11 @@ public:
|
||||
template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
|
||||
void fill(const Iterable& args, const weight_type<T>& weights,
|
||||
const sample_type<U>& samples) {
|
||||
std::lock_guard<mutex_type> guard{storage_and_mutex_.second()};
|
||||
std::lock_guard<typename mutex_base_t::type> guard{mutex_base_t::get()};
|
||||
mp11::tuple_apply(
|
||||
[&](const auto&... sargs) {
|
||||
detail::fill_n(offset_, storage_and_mutex_.first(), axes_,
|
||||
detail::make_span(args), detail::to_ptr_size(weights.value),
|
||||
detail::fill_n(offset_, storage_, axes_, detail::make_span(args),
|
||||
detail::to_ptr_size(weights.value),
|
||||
detail::to_ptr_size(sargs)...);
|
||||
},
|
||||
samples.value);
|
||||
@ -342,8 +311,8 @@ public:
|
||||
const auto idx = detail::at(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
BOOST_ASSERT(idx < storage_and_mutex_.first().size());
|
||||
return storage_and_mutex_.first()[idx];
|
||||
BOOST_ASSERT(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices stored in `std::tuple` (read-only).
|
||||
@ -355,8 +324,8 @@ public:
|
||||
const auto idx = detail::at(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
BOOST_ASSERT(idx < storage_and_mutex_.first().size());
|
||||
return storage_and_mutex_.first()[idx];
|
||||
BOOST_ASSERT(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices stored in iterable.
|
||||
@ -368,8 +337,8 @@ public:
|
||||
const auto idx = detail::at(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
BOOST_ASSERT(idx < storage_and_mutex_.first().size());
|
||||
return storage_and_mutex_.first()[idx];
|
||||
BOOST_ASSERT(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access cell value at integral indices stored in iterable (read-only).
|
||||
@ -381,8 +350,8 @@ public:
|
||||
const auto idx = detail::at(axes_, is);
|
||||
if (!is_valid(idx))
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
|
||||
BOOST_ASSERT(idx < storage_and_mutex_.first().size());
|
||||
return storage_and_mutex_.first()[idx];
|
||||
BOOST_ASSERT(idx < storage_.size());
|
||||
return storage_[idx];
|
||||
}
|
||||
|
||||
/// Access value at index (number for rank = 1, else `std::tuple` or iterable).
|
||||
@ -403,7 +372,7 @@ public:
|
||||
// testing offset is redundant, but offers fast return if it fails
|
||||
return offset_ == unsafe_access::offset(rhs) &&
|
||||
detail::axes_equal(axes_, unsafe_access::axes(rhs)) &&
|
||||
storage_and_mutex_.first() == unsafe_access::storage(rhs);
|
||||
storage_ == unsafe_access::storage(rhs);
|
||||
}
|
||||
|
||||
/// Negation of the equality operator.
|
||||
@ -413,89 +382,97 @@ public:
|
||||
}
|
||||
|
||||
/// Add values of another histogram.
|
||||
template <class A, class S,
|
||||
class = std::enable_if_t<detail::has_operator_radd<
|
||||
value_type, typename histogram<A, S>::value_type>::value>>
|
||||
histogram& operator+=(const histogram<A, S>& rhs) {
|
||||
template <class A, class S>
|
||||
std::enable_if_t<
|
||||
detail::has_operator_radd<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
operator+=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
auto& s = storage_and_mutex_.first();
|
||||
std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x += *rit++; });
|
||||
for (auto&& x : storage_) x += *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtract values of another histogram.
|
||||
template <class A, class S,
|
||||
class = std::enable_if_t<detail::has_operator_rsub<
|
||||
value_type, typename histogram<A, S>::value_type>::value>>
|
||||
histogram& operator-=(const histogram<A, S>& rhs) {
|
||||
template <class A, class S>
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rsub<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
operator-=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
auto& s = storage_and_mutex_.first();
|
||||
std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x -= *rit++; });
|
||||
for (auto&& x : storage_) x -= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply by values of another histogram.
|
||||
template <class A, class S,
|
||||
class = std::enable_if_t<detail::has_operator_rmul<
|
||||
value_type, typename histogram<A, S>::value_type>::value>>
|
||||
histogram& operator*=(const histogram<A, S>& rhs) {
|
||||
template <class A, class S>
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rmul<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
operator*=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
auto& s = storage_and_mutex_.first();
|
||||
std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x *= *rit++; });
|
||||
for (auto&& x : storage_) x *= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Divide by values of another histogram.
|
||||
template <class A, class S,
|
||||
class = std::enable_if_t<detail::has_operator_rdiv<
|
||||
value_type, typename histogram<A, S>::value_type>::value>>
|
||||
histogram& operator/=(const histogram<A, S>& rhs) {
|
||||
template <class A, class S>
|
||||
std::enable_if_t<
|
||||
detail::has_operator_rdiv<value_type, typename histogram<A, S>::value_type>::value,
|
||||
histogram&>
|
||||
operator/=(const histogram<A, S>& rhs) {
|
||||
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
|
||||
auto rit = unsafe_access::storage(rhs).begin();
|
||||
auto& s = storage_and_mutex_.first();
|
||||
std::for_each(s.begin(), s.end(), [&rit](auto&& x) { x /= *rit++; });
|
||||
for (auto&& x : storage_) x /= *rit++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply all values with a scalar.
|
||||
template <class V = value_type,
|
||||
class = std::enable_if_t<detail::has_operator_rmul<V, double>::value>>
|
||||
histogram& operator*=(const double x) {
|
||||
template <class V = value_type>
|
||||
std::enable_if_t<(detail::has_operator_rmul<V, double>::value &&
|
||||
detail::has_operator_rmul<storage_type, double>::value == true),
|
||||
histogram&>
|
||||
operator*=(const double x) {
|
||||
// use special implementation of scaling if available
|
||||
detail::static_if<detail::has_operator_rmul<storage_type, double>>(
|
||||
[](storage_type& s, auto x) { s *= x; },
|
||||
[](storage_type& s, auto x) {
|
||||
for (auto&& si : s) si *= x;
|
||||
},
|
||||
storage_and_mutex_.first(), x);
|
||||
storage_ *= x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply all values with a scalar.
|
||||
template <class V = value_type>
|
||||
std::enable_if_t<(detail::has_operator_rmul<V, double>::value &&
|
||||
detail::has_operator_rmul<storage_type, double>::value == false),
|
||||
histogram&>
|
||||
operator*=(const double x) {
|
||||
// generic implementation of scaling
|
||||
for (auto&& si : storage_) si *= x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Divide all values by a scalar.
|
||||
template <class V = value_type,
|
||||
class = std::enable_if_t<detail::has_operator_rmul<V, double>::value>>
|
||||
histogram& operator/=(const double x) {
|
||||
template <class V = value_type>
|
||||
std::enable_if_t<detail::has_operator_rmul<V, double>::value, histogram&> operator/=(
|
||||
const double x) {
|
||||
return operator*=(1.0 / x);
|
||||
}
|
||||
|
||||
/// Return value iterator to the beginning of the histogram.
|
||||
iterator begin() noexcept { return storage_and_mutex_.first().begin(); }
|
||||
iterator begin() noexcept { return storage_.begin(); }
|
||||
|
||||
/// Return value iterator to the end in the histogram.
|
||||
iterator end() noexcept { return storage_and_mutex_.first().end(); }
|
||||
iterator end() noexcept { return storage_.end(); }
|
||||
|
||||
/// Return value iterator to the beginning of the histogram (read-only).
|
||||
const_iterator begin() const noexcept { return storage_and_mutex_.first().begin(); }
|
||||
const_iterator begin() const noexcept { return storage_.begin(); }
|
||||
|
||||
/// Return value iterator to the end in the histogram (read-only).
|
||||
const_iterator end() const noexcept { return storage_and_mutex_.first().end(); }
|
||||
const_iterator end() const noexcept { return storage_.end(); }
|
||||
|
||||
/// Return value iterator to the beginning of the histogram (read-only).
|
||||
const_iterator cbegin() const noexcept { return begin(); }
|
||||
@ -506,7 +483,7 @@ public:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
detail::axes_serialize(ar, axes_);
|
||||
ar& make_nvp("storage", storage_and_mutex_.first());
|
||||
ar& make_nvp("storage", storage_);
|
||||
if (Archive::is_loading::value) {
|
||||
offset_ = detail::offset(axes_);
|
||||
detail::throw_if_axes_is_too_large(axes_);
|
||||
@ -515,13 +492,7 @@ public:
|
||||
|
||||
private:
|
||||
axes_type axes_;
|
||||
|
||||
using mutex_type = mp11::mp_if_c<(storage_type::has_threading_support &&
|
||||
detail::has_growing_axis<axes_type>::value),
|
||||
std::mutex, detail::noop_mutex>;
|
||||
|
||||
detail::compressed_pair<storage_type, mutex_type> storage_and_mutex_;
|
||||
|
||||
storage_type storage_;
|
||||
std::size_t offset_ = 0;
|
||||
|
||||
friend struct unsafe_access;
|
||||
|
@ -69,13 +69,13 @@ struct unsafe_access {
|
||||
*/
|
||||
template <class Histogram>
|
||||
static auto& storage(Histogram& hist) {
|
||||
return hist.storage_and_mutex_.first();
|
||||
return hist.storage_;
|
||||
}
|
||||
|
||||
/// @copydoc storage()
|
||||
template <class Histogram>
|
||||
static const auto& storage(const Histogram& hist) {
|
||||
return hist.storage_and_mutex_.first();
|
||||
return hist.storage_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,8 +84,6 @@ boost_test(TYPE run SOURCES detail_axes_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_convert_integer_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_compressed_pair_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_detect_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES detail_limits_test.cpp
|
||||
|
@ -53,7 +53,6 @@ alias cxx14 :
|
||||
[ run detail_args_type_test.cpp ]
|
||||
[ run detail_axes_test.cpp ]
|
||||
[ run detail_convert_integer_test.cpp ]
|
||||
[ run detail_compressed_pair_test.cpp ]
|
||||
[ run detail_detect_test.cpp ]
|
||||
[ run detail_limits_test.cpp ]
|
||||
[ run detail_make_default_test.cpp ]
|
||||
|
@ -21,6 +21,8 @@
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
BOOST_TEST(std::is_nothrow_move_constructible<axis::category<int>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_constructible<axis::category<std::string>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_assignable<axis::category<int>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_assignable<axis::category<std::string>>::value);
|
||||
|
||||
|
@ -18,6 +18,7 @@ using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
BOOST_TEST(std::is_nothrow_move_assignable<axis::integer<>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_constructible<axis::integer<>>::value);
|
||||
|
||||
// bad_ctor
|
||||
{
|
||||
|
@ -22,6 +22,7 @@ using def = use_default;
|
||||
|
||||
int main() {
|
||||
BOOST_TEST(std::is_nothrow_move_assignable<axis::regular<>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_constructible<axis::regular<>>::value);
|
||||
|
||||
// bad_ctors
|
||||
{
|
||||
|
@ -20,6 +20,7 @@ using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
BOOST_TEST(std::is_nothrow_move_assignable<axis::variable<>>::value);
|
||||
BOOST_TEST(std::is_nothrow_move_constructible<axis::variable<>>::value);
|
||||
|
||||
// bad_ctors
|
||||
{
|
||||
|
@ -1,42 +0,0 @@
|
||||
// Copyright 2019 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include "std_ostream.hpp"
|
||||
|
||||
using namespace boost::histogram::detail;
|
||||
|
||||
struct nothrow_move {
|
||||
nothrow_move(nothrow_move&&) noexcept {}
|
||||
nothrow_move& operator=(nothrow_move&&) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct throw_move {
|
||||
throw_move(throw_move&&) {}
|
||||
throw_move& operator=(throw_move&&) { return *this; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_nothrow_move_constructible<compressed_pair<nothrow_move, nothrow_move> >));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE(
|
||||
(std::is_nothrow_move_constructible<compressed_pair<nothrow_move, throw_move> >));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE(
|
||||
(std::is_nothrow_move_constructible<compressed_pair<throw_move, nothrow_move> >));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE(
|
||||
(std::is_nothrow_move_constructible<compressed_pair<throw_move, throw_move> >));
|
||||
|
||||
BOOST_TEST_GE(sizeof(compressed_pair<int, char>), sizeof(int) + sizeof(char));
|
||||
BOOST_TEST_EQ(sizeof(compressed_pair<int, nothrow_move>), sizeof(int));
|
||||
BOOST_TEST_EQ(sizeof(compressed_pair<int, throw_move>), sizeof(int));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user