More doc improvements

This commit is contained in:
Hans Dembinski 2019-04-28 12:22:49 +02:00 committed by GitHub
parent 22ec005290
commit 073dffdf09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 278 additions and 178 deletions

View File

@ -40,7 +40,6 @@ actions doxygen_postprocessing
boostbook histogram boostbook histogram
: :
histogram.qbk histogram.qbk
reference_pp.xml
: :
<xsl:param>boost.root=../../../.. <xsl:param>boost.root=../../../..
<xsl:param>boost.libraries=../../../libraries.htm <xsl:param>boost.libraries=../../../libraries.htm
@ -48,6 +47,7 @@ boostbook histogram
<xsl:param>chunk.first.sections=1 <xsl:param>chunk.first.sections=1
<xsl:param>generate.section.toc.level=1 <xsl:param>generate.section.toc.level=1
<xsl:param>generate.toc="chapter nop section nop" <xsl:param>generate.toc="chapter nop section nop"
<dependency>reference_pp.xml
; ;
alias boostdoc ; alias boostdoc ;

View File

@ -1,50 +1,95 @@
from __future__ import print_function
import sys import sys
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import re
def log(*args):
print("PP:", *args)
def select(condition, *tags):
result = []
for tag in tags:
for item in root.iter(tag):
if condition(item):
result.append(item)
return result
def is_detail(x):
if x.text is not None:
if "detail" in x.text:
return True
m = re.match("(?:typename)? *([A-Za-z0-9_\:]+)", x.text)
if m is not None:
s = m.group(1)
if s.startswith("detail") or s.endswith("_impl"):
x.text = s
return True
p = x.find("purpose")
if p is not None:
return p.text.lower().strip() == "implementation detail"
return False
tree = ET.parse(sys.argv[1]) tree = ET.parse(sys.argv[1])
root = tree.getroot() root = tree.getroot()
parent_map = {c:p for p in tree.iter() for c in p} parent_map = {c:p for p in tree.iter() for c in p}
# hide all unnamed template parameters, these are used for SFINAE unspecified = ET.Element("emphasis")
for item in root.iter("template-type-parameter"): unspecified.text = "unspecified"
if not item.get("name"):
parent = parent_map[item]
assert parent.tag == "template"
parent.remove(item)
# replace any type with "detail" in its name with "implementation_defined" # hide all unnamed template parameters, these are used for SFINAE
for item in root.iter("type"): for item in select(lambda x: x.get("name") == "", "template-type-parameter"):
if not item.text: parent = parent_map[item]
continue assert parent.tag == "template"
if "detail" in item.text: parent.remove(item)
item.text = "implementation_defined" parent = parent_map[parent]
name = parent.get("name")
if name is None:
log("removing unnamed template parameter from", parent.tag)
else:
log("removing unnamed template parameter from", parent.tag, name)
# replace any type with "detail" in its name with "unspecified"
for item in select(is_detail, "type"):
log("replacing", '"%s"' % item.text, 'with "unspecified"')
item.clear()
item.append(unspecified)
# hide private member functions # hide private member functions
for item in root.iter("method-group"): for item in select(lambda x: x.get("name") == "private member functions", "method-group"):
if item.get("name") == "private member functions": parent = parent_map[item]
parent_map[item].remove(item) log("removing private member functions from", parent.tag, parent.get("name"))
parent.remove(item)
# hide undocumented classes, structs, functions and replace those declared "implementation detail" with typedef to implementation_defined # hide undocumented classes, structs, functions and replace those declared
for tag in ("class", "struct", "function"): # "implementation detail" with typedef to implementation_defined
for item in root.iter(tag): for item in select(lambda x:True, "class", "struct", "function"):
purpose = item.find("purpose") purpose = item.find("purpose")
if purpose is None: if purpose is None:
parent_map[item].remove(item) parent = parent_map[item]
elif purpose.text.strip().lower() == "implementation detail": log("removing undocumented", item.tag, item.get("name"), "from",
name = item.get("name") parent.tag, parent.get("name"))
item.clear() parent_map[item].remove(item)
item.tag = "typedef" elif purpose.text.strip().lower() == "implementation detail":
item.set("name", name) log("replacing", item.tag, item.get("name"), "with unspecified typedef")
type = ET.Element("type") name = item.get("name")
type.text = "implementation_defined" item.clear()
item.append(type) item.tag = "typedef"
item.set("name", name)
type = ET.Element("type")
type.append(unspecified)
item.append(type)
# hide methods and constructors explicitly declared as "implementation detail" # hide methods and constructors explicitly declared as "implementation detail"
for tag in ("constructor", "method"): for item in select(is_detail, "constructor", "method"):
for item in root.iter(tag): name = item.get("name")
purpose = item.find("purpose") log("removing", (item.tag + " " + name) if name is not None else item.tag,
if purpose is not None and purpose.text.strip() == "implementation detail": "declared as implementation detail")
parent_map[item].remove(item) parent_map[item].remove(item)
tree.write(sys.argv[2]) tree.write(sys.argv[2])

View File

@ -26,51 +26,20 @@ using common_axes = mp11::mp_cond<
is_sequence_of_axis<U>, U, is_sequence_of_axis<U>, U,
std::true_type, T std::true_type, T
>; >;
template <class T, class U>
using common_container = mp11::mp_cond<
is_array_like<T>, T,
is_array_like<U>, U,
is_vector_like<T>, T,
is_vector_like<U>, U,
std::true_type, T
>;
// clang-format on // clang-format on
template <class T> // Non-PODs rank highest, then floats, than integers; types with more capacity are higher
using type_score = mp11::mp_size_t<((!std::is_pod<T>::value) * 1000 + template <class Storage>
std::is_floating_point<T>::value * 50 + sizeof(T))>; static constexpr std::size_t type_rank() {
using T = typename Storage::value_type;
return !std::is_pod<T>::value * 10000 + std::is_floating_point<T>::value * 100 +
10 * sizeof(T) + 2 * is_array_like<Storage>::value +
is_vector_like<Storage>::value;
;
}
template <class T, class U> template <class T, class U>
struct common_storage_impl; using common_storage = mp11::mp_if_c<(type_rank<T>() >= type_rank<U>()), T, U>;
template <class T, class U>
struct common_storage_impl<storage_adaptor<T>, storage_adaptor<U>> {
using type =
mp11::mp_if_c<(type_score<typename storage_adaptor<T>::value_type>::value >=
type_score<typename storage_adaptor<U>::value_type>::value),
storage_adaptor<T>, storage_adaptor<U>>;
};
template <class T, class A>
struct common_storage_impl<storage_adaptor<T>, unlimited_storage<A>> {
using type =
mp11::mp_if_c<(type_score<typename storage_adaptor<T>::value_type>::value >=
type_score<typename unlimited_storage<A>::value_type>::value),
storage_adaptor<T>, unlimited_storage<A>>;
};
template <class C, class A>
struct common_storage_impl<unlimited_storage<A>, storage_adaptor<C>>
: common_storage_impl<storage_adaptor<C>, unlimited_storage<A>> {};
template <class A1, class A2>
struct common_storage_impl<unlimited_storage<A1>, unlimited_storage<A2>> {
using type = unlimited_storage<A1>;
};
template <class A, class B>
using common_storage = typename common_storage_impl<A, B>::type;
} // namespace detail } // namespace detail
} // namespace histogram } // namespace histogram
} // namespace boost } // namespace boost

View File

@ -215,9 +215,18 @@ BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul,
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv,
(std::declval<T&>() /= std::declval<U&>())); (std::declval<T&>() /= std::declval<U&>()));
BOOST_HISTOGRAM_DETECT(has_threading_support, (T::has_threading_support));
template <typename T> template <typename T>
using is_storage = using is_storage = mp11::mp_and<is_indexable_container<T>, has_method_reset<T>,
mp11::mp_bool<(is_indexable_container<T>::value && has_method_reset<T>::value)>; has_threading_support<T>>;
template <class T>
using is_adaptible = mp11::mp_or<is_vector_like<T>, is_array_like<T>, is_map_like<T>>;
template <class T, class _ = remove_cvref_t<T>,
class = std::enable_if_t<(is_storage<_>::value || is_adaptible<_>::value)>>
struct requires_storage_or_adaptible {};
template <typename T> template <typename T>
struct is_tuple_impl : std::false_type {}; struct is_tuple_impl : std::false_type {};

View File

@ -15,6 +15,7 @@
#include <boost/histogram/detail/noop_mutex.hpp> #include <boost/histogram/detail/noop_mutex.hpp>
#include <boost/histogram/detail/static_if.hpp> #include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp> #include <boost/histogram/fwd.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/mp11/list.hpp> #include <boost/mp11/list.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
#include <mutex> #include <mutex>
@ -22,6 +23,7 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector>
namespace boost { namespace boost {
namespace histogram { namespace histogram {
@ -135,7 +137,7 @@ public:
} }
/// Get N-th axis with run-time number. /// Get N-th axis with run-time number.
/// Use the version that accepts a compile-time number, if possible. /// Prefer the version that accepts a compile-time number, if you can use it.
decltype(auto) axis(unsigned i) const { decltype(auto) axis(unsigned i) const {
detail::axis_index_is_valid(axes_, i); detail::axis_index_is_valid(axes_, i);
return detail::axis_get(axes_, i); return detail::axis_get(axes_, i);
@ -153,13 +155,6 @@ public:
not convertible to the value type accepted by the axis or passing the wrong number not convertible to the value type accepted by the axis or passing the wrong number
of arguments causes a throw of `std::invalid_argument`. of arguments causes a throw of `std::invalid_argument`.
__Axis with multiple arguments__
If the histogram contains an axis which accepts a `std::tuple` of arguments, the
arguments for that axis need to passed as a `std::tuple`, for example,
`std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
the arguments can be passed directly.
__Optional weight__ __Optional weight__
An optional weight can be passed as the first or last argument An optional weight can be passed as the first or last argument
@ -173,83 +168,93 @@ public:
[sample](boost/histogram/sample.html) helper function can pass one or more arguments to [sample](boost/histogram/sample.html) helper function can pass one or more arguments to
the storage element. If samples and weights are used together, they can be passed in the storage element. If samples and weights are used together, they can be passed in
any order at the beginning or end of the argument list. any order at the beginning or end of the argument list.
__Axis with multiple arguments__
If the histogram contains an axis which accepts a `std::tuple` of arguments, the
arguments for that axis need to passed as a `std::tuple`, for example,
`std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
the arguments can be passed directly.
*/ */
template <class... Ts> template <class... Ts>
auto operator()(const Ts&... ts) { iterator operator()(const Ts&... ts) {
return operator()(std::make_tuple(ts...)); return operator()(std::make_tuple(ts...));
} }
/// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`. /// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
template <class... Ts> template <class... Ts>
auto operator()(const std::tuple<Ts...>& t) { iterator operator()(const std::tuple<Ts...>& t) {
return detail::fill(axes_, storage_and_mutex_, t); return detail::fill(axes_, storage_and_mutex_, t);
} }
/// Access cell value at integral indices. /** Access cell value at integral indices.
/**
You can pass indices as individual arguments, as a std::tuple of integers, or as an You can pass indices as individual arguments, as a std::tuple of integers, or as an
interable range of integers. Passing the wrong number of arguments causes a throw of interable range of integers. Passing the wrong number of arguments causes a throw of
std::invalid_argument. Passing an index which is out of bounds causes a throw of std::invalid_argument. Passing an index which is out of bounds causes a throw of
std::out_of_range. std::out_of_range.
@param i index of first axis.
@param is indices of second, third, ... axes.
@returns reference to cell value.
*/ */
template <class... Ts> template <class... Indices>
decltype(auto) at(axis::index_type t, Ts... ts) { decltype(auto) at(axis::index_type i, Indices... is) {
return at(std::forward_as_tuple(t, ts...)); return at(std::make_tuple(i, is...));
} }
/// Access cell value at integral indices (read-only). /// Access cell value at integral indices (read-only).
/// @copydoc at(axis::index_type t, Ts... ts) template <class... Indices>
template <class... Ts> decltype(auto) at(axis::index_type i, Indices... is) const {
decltype(auto) at(axis::index_type t, Ts... ts) const { return at(std::make_tuple(i, is...));
return at(std::forward_as_tuple(t, ts...));
} }
/// Access cell value at integral indices stored in `std::tuple`. /// Access cell value at integral indices stored in `std::tuple`.
/// @copydoc at(axis::index_type t, Ts... ts) template <typename... Indices>
template <typename... Ts> decltype(auto) at(const std::tuple<axis::index_type, Indices...>& is) {
decltype(auto) at(const std::tuple<Ts...>& t) { const auto idx = detail::at(axes_, is);
const auto idx = detail::at(axes_, t); if (!idx)
if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds")); BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
return storage_and_mutex_.first()[*idx]; return storage_and_mutex_.first()[*idx];
} }
/// Access cell value at integral indices stored in `std::tuple` (read-only). /// Access cell value at integral indices stored in `std::tuple` (read-only).
/// @copydoc at(axis::index_type t, Ts... ts) template <typename... Indices>
template <typename... Ts> decltype(auto) at(const std::tuple<axis::index_type, Indices...>& is) const {
decltype(auto) at(const std::tuple<Ts...>& t) const { const auto idx = detail::at(axes_, is);
const auto idx = detail::at(axes_, t); if (!idx)
if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds")); BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
return storage_and_mutex_.first()[*idx]; return storage_and_mutex_.first()[*idx];
} }
/// Access cell value at integral indices stored in iterable. /// Access cell value at integral indices stored in iterable.
/// @copydoc at(axis::index_type t, Ts... ts)
template <class Iterable, class = detail::requires_iterable<Iterable>> template <class Iterable, class = detail::requires_iterable<Iterable>>
decltype(auto) at(const Iterable& c) { decltype(auto) at(const Iterable& is) {
const auto idx = detail::at(axes_, c); const auto idx = detail::at(axes_, is);
if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds")); if (!idx)
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
return storage_and_mutex_.first()[*idx]; return storage_and_mutex_.first()[*idx];
} }
/// Access cell value at integral indices stored in iterable (read-only). /// Access cell value at integral indices stored in iterable (read-only).
/// @copydoc at(axis::index_type t, Ts... ts)
template <class Iterable, class = detail::requires_iterable<Iterable>> template <class Iterable, class = detail::requires_iterable<Iterable>>
decltype(auto) at(const Iterable& c) const { decltype(auto) at(const Iterable& is) const {
const auto idx = detail::at(axes_, c); const auto idx = detail::at(axes_, is);
if (!idx) BOOST_THROW_EXCEPTION(std::out_of_range("indices out of bounds")); if (!idx)
BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
return storage_and_mutex_.first()[*idx]; return storage_and_mutex_.first()[*idx];
} }
/// Access value at index (number for rank = 1, else `std::tuple` or iterable). /// Access value at index (number for rank = 1, else `std::tuple` or iterable).
template <class T> template <class Indices>
decltype(auto) operator[](const T& t) { decltype(auto) operator[](const Indices& is) {
return at(t); return at(is);
} }
/// @copydoc operator[] /// Access value at index (read-only).
template <class T> template <class Indices>
decltype(auto) operator[](const T& t) const { decltype(auto) operator[](const Indices& is) const {
return at(t); return at(is);
} }
/// Equality operator, tests equality for all axes and the storage. /// Equality operator, tests equality for all axes and the storage.
@ -266,11 +271,10 @@ public:
} }
/// Add values of another histogram. /// Add values of another histogram.
template <class A, class S> 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) { histogram& operator+=(const histogram<A, S>& rhs) {
static_assert(detail::has_operator_radd<value_type,
typename histogram<A, S>::value_type>::value,
"cell value does not support adding value of right-hand-side");
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs))) if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ")); BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
auto rit = unsafe_access::storage(rhs).begin(); auto rit = unsafe_access::storage(rhs).begin();
@ -280,11 +284,10 @@ public:
} }
/// Subtract values of another histogram. /// Subtract values of another histogram.
template <class A, class S> 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) { histogram& operator-=(const histogram<A, S>& rhs) {
static_assert(detail::has_operator_rsub<value_type,
typename histogram<A, S>::value_type>::value,
"cell value does not support subtracting value of right-hand-side");
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs))) if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ")); BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
auto rit = unsafe_access::storage(rhs).begin(); auto rit = unsafe_access::storage(rhs).begin();
@ -294,11 +297,10 @@ public:
} }
/// Multiply by values of another histogram. /// Multiply by values of another histogram.
template <class A, class S> 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) { histogram& operator*=(const histogram<A, S>& rhs) {
static_assert(detail::has_operator_rmul<value_type,
typename histogram<A, S>::value_type>::value,
"cell value does not support multiplying by value of right-hand-side");
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs))) if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ")); BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
auto rit = unsafe_access::storage(rhs).begin(); auto rit = unsafe_access::storage(rhs).begin();
@ -308,11 +310,10 @@ public:
} }
/// Divide by values of another histogram. /// Divide by values of another histogram.
template <class A, class S> 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) { histogram& operator/=(const histogram<A, S>& rhs) {
static_assert(detail::has_operator_rdiv<value_type,
typename histogram<A, S>::value_type>::value,
"cell value does not support dividing by value of right-hand-side");
if (!detail::axes_equal(axes_, unsafe_access::axes(rhs))) if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ")); BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
auto rit = unsafe_access::storage(rhs).begin(); auto rit = unsafe_access::storage(rhs).begin();
@ -372,41 +373,72 @@ private:
friend struct unsafe_access; friend struct unsafe_access;
}; };
/**
Pairwise add cells of two histograms and return histogram with the sum.
The returned histogram type is the most efficient and safest one constructible from the
inputs, if they are not the same type. If one histogram has a tuple axis, the result has
a tuple axis. The chosen storage is the one with the larger dynamic range.
*/
template <class A1, class S1, class A2, class S2> template <class A1, class S1, class A2, class S2>
auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) { auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a); auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
return r += b; return r += b;
} }
/** Pairwise multiply cells of two histograms and return histogram with the product.
For notes on the returned histogram type, see operator+.
*/
template <class A1, class S1, class A2, class S2> template <class A1, class S1, class A2, class S2>
auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) { auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a); auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
return r *= b; return r *= b;
} }
/** Pairwise subtract cells of two histograms and return histogram with the difference.
For notes on the returned histogram type, see operator+.
*/
template <class A1, class S1, class A2, class S2> template <class A1, class S1, class A2, class S2>
auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) { auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a); auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
return r -= b; return r -= b;
} }
/** Pairwise divide cells of two histograms and return histogram with the quotient.
For notes on the returned histogram type, see operator+.
*/
template <class A1, class S1, class A2, class S2> template <class A1, class S1, class A2, class S2>
auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) { auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a); auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
return r /= b; return r /= b;
} }
/** Multiply all cells of the histogram by a number and return a new histogram.
If the original histogram has integer cells, the result has double cells.
*/
template <class A, class S> template <class A, class S>
auto operator*(const histogram<A, S>& h, double x) { auto operator*(const histogram<A, S>& h, double x) {
auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h); auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h);
return r *= x; return r *= x;
} }
/** Multiply all cells of the histogram by a number and return a new histogram.
If the original histogram has integer cells, the result has double cells.
*/
template <class A, class S> template <class A, class S>
auto operator*(double x, const histogram<A, S>& h) { auto operator*(double x, const histogram<A, S>& h) {
return h * x; return h * x;
} }
/** Divide all cells of the histogram by a number and return a new histogram.
If the original histogram has integer cells, the result has double cells.
*/
template <class A, class S> template <class A, class S>
auto operator/(const histogram<A, S>& h, double x) { auto operator/(const histogram<A, S>& h, double x) {
return h * (1.0 / x); return h * (1.0 / x);
@ -423,21 +455,24 @@ histogram(Axes&& axes, Storage&& storage)
#endif #endif
/// Helper function to mark argument as weight. /** Helper function to mark argument as weight.
/// @param t argument to be forward to the histogram.
@param t argument to be forward to the histogram.
*/
template <typename T> template <typename T>
auto weight(T&& t) noexcept { auto weight(T&& t) noexcept {
return weight_type<T>{std::forward<T>(t)}; return weight_type<T>{std::forward<T>(t)};
} }
/// Helper function to mark arguments as sample. /** Helper function to mark arguments as sample.
/// @param ts arguments to be forwarded to the accumulator.
@param ts arguments to be forwarded to the accumulator.
*/
template <typename... Ts> template <typename... Ts>
auto sample(Ts&&... ts) noexcept { auto sample(Ts&&... ts) noexcept {
return sample_type<std::tuple<Ts...>>{std::forward_as_tuple(std::forward<Ts>(ts)...)}; return sample_type<std::tuple<Ts...>>{std::forward_as_tuple(std::forward<Ts>(ts)...)};
} }
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
template <class T> template <class T>
struct weight_type { struct weight_type {
T value; T value;
@ -447,7 +482,6 @@ template <class T>
struct sample_type { struct sample_type {
T value; T value;
}; };
#endif
} // namespace histogram } // namespace histogram
} // namespace boost } // namespace boost

View File

@ -30,7 +30,9 @@ namespace histogram {
@param axis First axis instance. @param axis First axis instance.
@param axes Other axis instances. @param axes Other axis instances.
*/ */
template <class Storage, class Axis, class... Axes, class = detail::requires_axis<Axis>> template <class Storage, class Axis, class... Axes,
class = detail::requires_storage_or_adaptible<Storage>,
class = detail::requires_axis<Axis>>
auto make_histogram_with(Storage&& storage, Axis&& axis, Axes&&... axes) { auto make_histogram_with(Storage&& storage, Axis&& axis, Axes&&... axes) {
auto a = std::make_tuple(std::forward<Axis>(axis), std::forward<Axes>(axes)...); auto a = std::make_tuple(std::forward<Axis>(axis), std::forward<Axes>(axes)...);
using U = detail::remove_cvref_t<Storage>; using U = detail::remove_cvref_t<Storage>;
@ -66,6 +68,7 @@ auto make_weighted_histogram(Axis&& axis, Axes&&... axes) {
@param iterable Iterable range of axis objects. @param iterable Iterable range of axis objects.
*/ */
template <class Storage, class Iterable, template <class Storage, class Iterable,
class = detail::requires_storage_or_adaptible<Storage>,
class = detail::requires_sequence_of_any_axis<Iterable>> class = detail::requires_sequence_of_any_axis<Iterable>>
auto make_histogram_with(Storage&& storage, Iterable&& iterable) { auto make_histogram_with(Storage&& storage, Iterable&& iterable) {
using U = detail::remove_cvref_t<Storage>; using U = detail::remove_cvref_t<Storage>;
@ -101,7 +104,9 @@ auto make_weighted_histogram(Iterable&& iterable) {
@param begin Iterator to range of axis objects. @param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects. @param end Iterator to range of axis objects.
*/ */
template <class Storage, class Iterator, class = detail::requires_iterator<Iterator>> template <class Storage, class Iterator,
class = detail::requires_storage_or_adaptible<Storage>,
class = detail::requires_iterator<Iterator>>
auto make_histogram_with(Storage&& storage, Iterator begin, Iterator end) { auto make_histogram_with(Storage&& storage, Iterator begin, Iterator end) {
using T = detail::remove_cvref_t<decltype(*begin)>; using T = detail::remove_cvref_t<decltype(*begin)>;
return make_histogram_with(std::forward<Storage>(storage), std::vector<T>(begin, end)); return make_histogram_with(std::forward<Storage>(storage), std::vector<T>(begin, end));

View File

@ -172,8 +172,8 @@ void serialize(Archive& ar, large_int<Allocator>& x, unsigned /* version */) {
template <class Archive, class T> template <class Archive, class T>
void serialize(Archive& ar, storage_adaptor<T>& x, unsigned /* version */) { void serialize(Archive& ar, storage_adaptor<T>& x, unsigned /* version */) {
using impl_t = typename storage_adaptor<T>::base_type; auto& impl = unsafe_access::storage_adaptor_impl(x);
ar& serialization::make_nvp("impl", static_cast<impl_t&>(x)); ar& serialization::make_nvp("impl", impl);
} }
template <class Allocator, class Archive> template <class Allocator, class Archive>

View File

@ -336,9 +336,9 @@ using storage_adaptor_impl =
/// Turns any vector-like, array-like, and map-like container into a storage type. /// Turns any vector-like, array-like, and map-like container into a storage type.
template <class T> template <class T>
class storage_adaptor : public detail::storage_adaptor_impl<T> { class storage_adaptor : public detail::storage_adaptor_impl<T> {
public: using impl_type = detail::storage_adaptor_impl<T>;
using base_type = detail::storage_adaptor_impl<T>;
public:
// standard copy, move, assign // standard copy, move, assign
storage_adaptor(storage_adaptor&&) = default; storage_adaptor(storage_adaptor&&) = default;
storage_adaptor(const storage_adaptor&) = default; storage_adaptor(const storage_adaptor&) = default;
@ -347,12 +347,12 @@ public:
// forwarding constructor // forwarding constructor
template <class... Ts> template <class... Ts>
storage_adaptor(Ts&&... ts) : base_type(std::forward<Ts>(ts)...) {} storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
// forwarding assign // forwarding assign
template <class U> template <class U>
storage_adaptor& operator=(U&& u) { storage_adaptor& operator=(U&& u) {
base_type::operator=(std::forward<U>(u)); impl_type::operator=(std::forward<U>(u));
return *this; return *this;
} }
@ -362,6 +362,9 @@ public:
using std::end; using std::end;
return std::equal(this->begin(), this->end(), begin(u), end(u), detail::equal{}); return std::equal(this->begin(), this->end(), begin(u), end(u), detail::equal{});
} }
private:
friend struct unsafe_access;
}; };
} // namespace histogram } // namespace histogram

View File

@ -132,7 +132,6 @@ public:
using large_int = detail::large_int< using large_int = detail::large_int<
typename std::allocator_traits<allocator_type>::template rebind_alloc<uint64_t>>; typename std::allocator_traits<allocator_type>::template rebind_alloc<uint64_t>>;
private:
struct buffer_type { struct buffer_type {
// cannot be moved outside of scope of unlimited_storage, large_int is dependent type // cannot be moved outside of scope of unlimited_storage, large_int is dependent type
using types = mp11::mp_list<uint8_t, uint16_t, uint32_t, uint64_t, large_int, double>; using types = mp11::mp_list<uint8_t, uint16_t, uint32_t, uint64_t, large_int, double>;
@ -246,12 +245,14 @@ private:
mutable void* ptr = nullptr; mutable void* ptr = nullptr;
}; };
public:
class reference; // forward declare to make friend of const_reference class reference; // forward declare to make friend of const_reference
/// implementation detail
class const_reference { class const_reference {
public: public:
const_reference(buffer_type& b, std::size_t i) : bref_(b), idx_(i) {} const_reference(buffer_type& b, std::size_t i) : bref_(b), idx_(i) {
BOOST_ASSERT(idx_ < bref_.size);
}
const_reference(const const_reference&) = default; const_reference(const const_reference&) = default;
@ -355,6 +356,7 @@ public:
friend class reference; friend class reference;
}; };
/// implementation detail
class reference : public const_reference { class reference : public const_reference {
public: public:
reference(buffer_type& b, std::size_t i) : const_reference(b, i) {} reference(buffer_type& b, std::size_t i) : const_reference(b, i) {}
@ -425,28 +427,28 @@ public:
private: private:
template <class Value, class Reference> template <class Value, class Reference>
class iterator_t : public detail::iterator_adaptor<iterator_t<Value, Reference>, class iterator_impl : public detail::iterator_adaptor<iterator_impl<Value, Reference>,
std::size_t, Reference, Value> { std::size_t, Reference, Value> {
public: public:
iterator_t() = default; iterator_impl() = default;
template <class V, class R> template <class V, class R>
iterator_t(const iterator_t<V, R>& it) iterator_impl(const iterator_impl<V, R>& it)
: iterator_t::iterator_adaptor_(it.base()), buffer_(it.buffer_) {} : iterator_impl::iterator_adaptor_(it.base()), buffer_(it.buffer_) {}
iterator_t(buffer_type* b, std::size_t i) noexcept iterator_impl(buffer_type* b, std::size_t i) noexcept
: iterator_t::iterator_adaptor_(i), buffer_(b) {} : iterator_impl::iterator_adaptor_(i), buffer_(b) {}
Reference operator*() const noexcept { return {*buffer_, this->base()}; } Reference operator*() const noexcept { return {*buffer_, this->base()}; }
template <class V, class R> template <class V, class R>
friend class iterator_t; friend class iterator_impl;
private: private:
mutable buffer_type* buffer_ = nullptr; mutable buffer_type* buffer_ = nullptr;
}; };
public: public:
using const_iterator = iterator_t<const value_type, const_reference>; using const_iterator = iterator_impl<const value_type, const_reference>;
using iterator = iterator_t<value_type, reference>; using iterator = iterator_impl<value_type, reference>;
explicit unlimited_storage(const allocator_type& a = {}) : buffer_(a) {} explicit unlimited_storage(const allocator_type& a = {}) : buffer_(a) {}
unlimited_storage(const unlimited_storage&) = default; unlimited_storage(const unlimited_storage&) = default;
@ -517,6 +519,7 @@ private:
struct incrementor { struct incrementor {
template <class T> template <class T>
void operator()(T* tp, buffer_type& b, std::size_t i) { void operator()(T* tp, buffer_type& b, std::size_t i) {
BOOST_ASSERT(tp && i < b.size);
if (!detail::safe_increment(tp[i])) { if (!detail::safe_increment(tp[i])) {
using U = detail::next_type<typename buffer_type::types, T>; using U = detail::next_type<typename buffer_type::types, T>;
b.template make<U>(b.size, tp); b.template make<U>(b.size, tp);

View File

@ -82,6 +82,15 @@ struct unsafe_access {
static constexpr auto& unlimited_storage_buffer(unlimited_storage<Allocator>& storage) { static constexpr auto& unlimited_storage_buffer(unlimited_storage<Allocator>& storage) {
return storage.buffer_; return storage.buffer_;
} }
/**
Get implementation of storage_adaptor.
@param storage instance of storage_adaptor.
*/
template <class T>
static constexpr auto& storage_adaptor_impl(storage_adaptor<T>& storage) {
return static_cast<typename storage_adaptor<T>::impl_type&>(storage);
}
}; };
} // namespace histogram } // namespace histogram

View File

@ -3,6 +3,8 @@
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
# keep in sync with Jamfile # keep in sync with Jamfile
boost_test(TYPE compile-fail SOURCES make_histogram_fail0.cpp)
boost_test(TYPE compile-fail SOURCES make_histogram_fail1.cpp)
boost_test(TYPE run SOURCES algorithm_project_test.cpp boost_test(TYPE run SOURCES algorithm_project_test.cpp
LIBRARIES Boost::histogram Boost::core) LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES algorithm_reduce_test.cpp boost_test(TYPE run SOURCES algorithm_reduce_test.cpp

View File

@ -34,6 +34,8 @@ project
; ;
alias cxx14 : alias cxx14 :
[ compile-fail make_histogram_fail0.cpp ]
[ compile-fail make_histogram_fail1.cpp ]
[ run algorithm_project_test.cpp ] [ run algorithm_project_test.cpp ]
[ run algorithm_reduce_test.cpp ] [ run algorithm_reduce_test.cpp ]
[ run algorithm_sum_test.cpp ] [ run algorithm_sum_test.cpp ]

View File

@ -134,19 +134,6 @@ int main() {
BOOST_TEST_THROWS(detail::bincount(v), std::overflow_error); BOOST_TEST_THROWS(detail::bincount(v), std::overflow_error);
} }
// common_container
{
using A = std::array<int, 10>;
using B = std::vector<int>;
using C = std::map<std::size_t, int>;
BOOST_TEST_TRAIT_SAME(detail::common_container<A, B>, A);
BOOST_TEST_TRAIT_SAME(detail::common_container<B, A>, A);
BOOST_TEST_TRAIT_SAME(detail::common_container<A, C>, A);
BOOST_TEST_TRAIT_SAME(detail::common_container<C, A>, A);
BOOST_TEST_TRAIT_SAME(detail::common_container<C, B>, B);
BOOST_TEST_TRAIT_SAME(detail::common_container<B, C>, B);
}
// common_storage // common_storage
{ {
BOOST_TEST_TRAIT_SAME( BOOST_TEST_TRAIT_SAME(

View File

@ -513,6 +513,8 @@ void run_tests() {
BOOST_TEST_EQ(h.at(j11), 1); BOOST_TEST_EQ(h.at(j11), 1);
BOOST_TEST_EQ(h[j11], 1); BOOST_TEST_EQ(h[j11], 1);
BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument); BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument);
int j13[] = {1, 3};
BOOST_TEST_THROWS((void)h.at(j13), std::out_of_range);
// tuple with weight // tuple with weight
h(std::make_tuple(weight(2), 0, 2.0)); h(std::make_tuple(weight(2), 0, 2.0));

View File

@ -0,0 +1,14 @@
// 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/histogram/make_histogram.hpp>
using namespace boost::histogram;
int main() {
// argument is not an axis
(void)make_histogram(std::vector<int>{});
}

View File

@ -0,0 +1,16 @@
// 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/histogram/axis/regular.hpp>
#include <boost/histogram/make_histogram.hpp>
#include <vector>
using namespace boost::histogram;
int main() {
// first and second arguments switched
(void)make_histogram_with(axis::regular<>(3, 0, 1), std::vector<int>{});
}