better solution for adding optional update method when axis has growth

This commit is contained in:
Hans Dembinski
2019-01-13 23:18:39 +01:00
parent 8e9dd85837
commit f1ac913246
6 changed files with 57 additions and 55 deletions

View File

@@ -22,29 +22,21 @@
namespace boost {
namespace histogram {
namespace axis {
/** Axis for an interval of integer values with unit steps.
*
* Binning is a O(1) operation. This axis operates
* faster than a regular axis.
*/
template <typename IntType, typename MetaData, option Options>
class integer : public base<MetaData, Options>,
public iterator_mixin<integer<IntType, MetaData, Options>> {
template <class IntType, class MetaData, option Options>
class integer_base : public base<MetaData, Options>,
public iterator_mixin<integer<IntType, MetaData, Options>> {
static_assert(!test(Options, option::circular) || !test(Options, option::underflow),
"circular axis cannot have underflow");
static_assert(!std::is_integral<IntType>::value || std::is_same<IntType, int>::value,
"integer axis requires type floating point type or int");
using base_type = base<MetaData, Options>;
public:
using metadata_type = MetaData;
using value_type = IntType;
private:
using index_type = std::conditional_t<std::is_integral<value_type>::value, int, double>;
public:
integer() = default;
integer_base() = default;
/** Construct over semi-open integer interval [start, stop).
*
@@ -53,13 +45,13 @@ public:
* \param metadata description of the axis.
* \param options extra bin options.
*/
integer(value_type start, value_type stop, metadata_type m = {})
integer_base(value_type start, value_type stop, metadata_type m = {})
: base_type(static_cast<unsigned>(stop - start > 0 ? stop - start : 0),
std::move(m))
, min_(start) {}
/// Constructor used by algorithm::reduce to shrink and rebin.
integer(const integer& src, int begin, int end, unsigned merge)
integer_base(const integer_base& src, int begin, int end, unsigned merge)
: base_type(end - begin, src.metadata()), min_(src.min_ + begin) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
@@ -72,11 +64,6 @@ public:
return index_impl(std::is_floating_point<value_type>(), x);
}
template <option O = Options, class = std::enable_if_t<test(O, option::growth)>>
auto update(value_type x) {
return update_impl(std::is_floating_point<value_type>(), x);
}
/// Returns axis value for index.
value_type value(index_type i) const noexcept {
if (!test(Options, option::circular)) {
@@ -90,11 +77,11 @@ public:
return subscript_impl(std::is_same<index_type, double>(), idx);
}
bool operator==(const integer& o) const noexcept {
bool operator==(const integer_base& o) const noexcept {
return base_type::operator==(o) && min_ == o.min_;
}
bool operator!=(const integer& o) const noexcept { return !operator==(o); }
bool operator!=(const integer_base& o) const noexcept { return !operator==(o); }
template <class Archive>
void serialize(Archive&, unsigned);
@@ -122,15 +109,54 @@ protected:
return base_type::size();
}
decltype(auto) subscript_impl(std::true_type, int idx) const noexcept {
return interval_view<integer_base>(*this, idx);
}
decltype(auto) subscript_impl(std::false_type, int idx) const noexcept {
return value(idx);
}
int min_ = 0;
};
/** Axis for an interval of integer values with unit steps.
*
* Binning is a O(1) operation. This axis operates
* faster than a regular axis.
*/
template <class IntType, class MetaData, option Options>
class integer : public integer_base<IntType, MetaData, Options> {
using base_type = integer_base<IntType, MetaData, Options>;
public:
using base_type::base_type;
};
template <class IntType, class MetaData>
class integer<IntType, MetaData, option::growth>
: public integer_base<IntType, MetaData, option::growth> {
using base_type = integer_base<IntType, MetaData, option::growth>;
using value_type = IntType;
public:
using base_type::base_type;
/// Returns index and shift (if axis has grown) for the passed argument.
auto update(value_type x) {
return update_impl(std::is_floating_point<value_type>(), x);
}
private:
auto update_impl(std::false_type, int x) noexcept {
const auto i = x - min_;
const auto i = x - base_type::min_;
if (i >= 0) {
if (i < base_type::size()) return std::make_pair(i, 0);
const auto n = i - base_type::size() + 1;
base_type::grow(n);
return std::make_pair(i, -n);
}
min_ += i;
base_type::min_ += i;
base_type::grow(-i);
return std::make_pair(0, -i);
}
@@ -142,16 +168,6 @@ protected:
BOOST_THROW_EXCEPTION(std::invalid_argument("argument is not finite"));
return std::make_pair(0, 0);
}
decltype(auto) subscript_impl(std::true_type, int idx) const noexcept {
return interval_view<integer>(*this, idx);
}
decltype(auto) subscript_impl(std::false_type, int idx) const noexcept {
return value(idx);
}
int min_ = 0;
};
#if __cpp_deduction_guides >= 201606

View File

@@ -174,9 +174,7 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, &T::lower);
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, &T::value);
BOOST_HISTOGRAM_MAKE_SFINAE(
has_method_update,
(std::declval<T>().update(std::declval<typename T::value_type&>())));
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_update, (&T::update));
template <typename T>
using get_value_method_return_type_impl = decltype(std::declval<T&>().value(0));

View File

@@ -172,7 +172,7 @@ void regular<T, Tr, M, O>::serialize(Archive& ar, unsigned /* version */) {
template <class T, class M, option O>
template <class Archive>
void integer<T, M, O>::serialize(Archive& ar, unsigned /* version */) {
void integer_base<T, M, O>::serialize(Archive& ar, unsigned /* version */) {
ar& static_cast<base_type&>(*this);
ar& min_;
}

View File

@@ -13,25 +13,25 @@ namespace histogram {
/// Unsafe read/write access to classes that potentially break consistency
struct unsafe_access {
/// Get axes
template <typename T>
template <class T>
static typename T::axes_type& axes(T& t) {
return t.axes_;
}
/// Get axes (const version)
template <typename T>
template <class T>
static const typename T::axes_type& axes(const T& t) {
return t.axes_;
}
/// Get storage
template <typename T>
template <class T>
static typename T::storage_type& storage(T& t) {
return t.storage_;
}
/// Get storage (const version)
template <typename T>
template <class T>
static const typename T::storage_type& storage(const T& t) {
return t.storage_;
}