Replace detail::compressed_pair with boost::core::empty_value

This commit is contained in:
Hans Dembinski 2019-10-24 21:47:17 +02:00 committed by GitHub
parent 5e24146062
commit ebabd550a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 283 additions and 465 deletions

View File

@ -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;

View File

@ -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>

View 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

View File

@ -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>

View File

@ -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;

View File

@ -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

View 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

View File

@ -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

View File

@ -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;

View File

@ -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_;
}
/**

View File

@ -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

View File

@ -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 ]

View File

@ -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);

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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();
}