1 // Copyright 2015-2017 Hans Dembinski
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // String representations here evaluate correctly in Python.
8 
9 #ifndef BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
10 #define BOOST_HISTOGRAM_AXIS_OSTREAM_HPP
11 
12 #include <boost/assert.hpp>
13 #include <boost/histogram/axis/regular.hpp>
14 #include <boost/histogram/detail/static_if.hpp>
15 #include <boost/histogram/detail/type_name.hpp>
16 #include <boost/histogram/fwd.hpp>
17 #include <boost/throw_exception.hpp>
18 #include <iomanip>
19 #include <iosfwd>
20 #include <sstream>
21 #include <stdexcept>
22 #include <type_traits>
23 
24 /**
25   \file boost/histogram/axis/ostream.hpp
26   Simple streaming operators for the builtin axis types.
27 
28   The text representation is not guaranteed to be stable between versions of
29   Boost.Histogram. This header is only included by
30   [boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
31   To you use your own, include your own implementation instead of this header and do not
32   include
33   [boost/histogram/ostream.hpp](histogram/reference.html#header.boost.histogram.ostream_hpp).
34  */
35 
36 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
37 
38 namespace boost {
39 namespace histogram {
40 
41 namespace detail {
axis_suffix(const axis::transform::id &)42 inline const char* axis_suffix(const axis::transform::id&) { return ""; }
axis_suffix(const axis::transform::log &)43 inline const char* axis_suffix(const axis::transform::log&) { return "_log"; }
axis_suffix(const axis::transform::sqrt &)44 inline const char* axis_suffix(const axis::transform::sqrt&) { return "_sqrt"; }
axis_suffix(const axis::transform::pow &)45 inline const char* axis_suffix(const axis::transform::pow&) { return "_pow"; }
46 
47 template <class OStream, class T>
stream_metadata(OStream & os,const T & t)48 void stream_metadata(OStream& os, const T& t) {
49   detail::static_if<detail::is_streamable<T>>(
50       [&os](const auto& t) {
51         std::ostringstream oss;
52         oss << t;
53         if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
54       },
55       [&os](const auto&) { os << ", metadata=" << detail::type_name<T>(); }, t);
56 }
57 
58 template <class OStream>
stream_options(OStream & os,const unsigned bits)59 void stream_options(OStream& os, const unsigned bits) {
60   os << ", options=";
61   bool first = true;
62 
63 #define BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(x) \
64   if (bits & axis::option::x) {                \
65     if (first)                                 \
66       first = false;                           \
67     else {                                     \
68       os << " | ";                             \
69     }                                          \
70     os << #x;                                  \
71   }
72 
73   BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(underflow);
74   BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(overflow);
75   BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(circular);
76   BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(growth);
77 
78 #undef BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM
79 
80   if (first) os << "none";
81 }
82 
83 template <class OStream, class T>
stream_transform(OStream &,const T &)84 void stream_transform(OStream&, const T&) {}
85 
86 template <class OStream>
stream_transform(OStream & os,const axis::transform::pow & t)87 void stream_transform(OStream& os, const axis::transform::pow& t) {
88   os << ", power=" << t.power;
89 }
90 
91 template <class OStream, class T>
stream_value(OStream & os,const T & t)92 void stream_value(OStream& os, const T& t) {
93   os << t;
94 }
95 
96 template <class OStream, class... Ts>
stream_value(OStream & os,const std::basic_string<Ts...> & t)97 void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
98   os << std::quoted(t);
99 }
100 
101 } // namespace detail
102 
103 namespace axis {
104 
105 template <class T>
106 class polymorphic_bin;
107 
108 template <class... Ts>
operator <<(std::basic_ostream<Ts...> & os,const null_type &)109 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const null_type&) {
110   return os; // do nothing
111 }
112 
113 template <class... Ts, class U>
operator <<(std::basic_ostream<Ts...> & os,const interval_view<U> & i)114 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
115                                       const interval_view<U>& i) {
116   os << "[" << i.lower() << ", " << i.upper() << ")";
117   return os;
118 }
119 
120 template <class... Ts, class U>
operator <<(std::basic_ostream<Ts...> & os,const polymorphic_bin<U> & i)121 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
122                                       const polymorphic_bin<U>& i) {
123   if (i.is_discrete())
124     os << static_cast<double>(i);
125   else
126     os << "[" << i.lower() << ", " << i.upper() << ")";
127   return os;
128 }
129 
130 template <class... Ts, class... Us>
operator <<(std::basic_ostream<Ts...> & os,const regular<Us...> & a)131 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
132                                       const regular<Us...>& a) {
133   os << "regular" << detail::axis_suffix(a.transform()) << "(" << a.size() << ", "
134      << a.value(0) << ", " << a.value(a.size());
135   detail::stream_metadata(os, a.metadata());
136   detail::stream_options(os, a.options());
137   detail::stream_transform(os, a.transform());
138   os << ")";
139   return os;
140 }
141 
142 template <class... Ts, class... Us>
operator <<(std::basic_ostream<Ts...> & os,const integer<Us...> & a)143 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
144                                       const integer<Us...>& a) {
145   os << "integer(" << a.value(0) << ", " << a.value(a.size());
146   detail::stream_metadata(os, a.metadata());
147   detail::stream_options(os, a.options());
148   os << ")";
149   return os;
150 }
151 
152 template <class... Ts, class... Us>
operator <<(std::basic_ostream<Ts...> & os,const variable<Us...> & a)153 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
154                                       const variable<Us...>& a) {
155   os << "variable(" << a.value(0);
156   for (index_type i = 1, n = a.size(); i <= n; ++i) { os << ", " << a.value(i); }
157   detail::stream_metadata(os, a.metadata());
158   detail::stream_options(os, a.options());
159   os << ")";
160   return os;
161 }
162 
163 template <class... Ts, class... Us>
operator <<(std::basic_ostream<Ts...> & os,const category<Us...> & a)164 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
165                                       const category<Us...>& a) {
166   os << "category(";
167   for (index_type i = 0, n = a.size(); i < n; ++i) {
168     detail::stream_value(os, a.value(i));
169     os << (i == (a.size() - 1) ? "" : ", ");
170   }
171   detail::stream_metadata(os, a.metadata());
172   detail::stream_options(os, a.options());
173   os << ")";
174   return os;
175 }
176 
177 template <class... Ts, class... Us>
operator <<(std::basic_ostream<Ts...> & os,const variant<Us...> & v)178 std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
179                                       const variant<Us...>& v) {
180   visit(
181       [&os](const auto& x) {
182         using A = std::decay_t<decltype(x)>;
183         detail::static_if<detail::is_streamable<A>>(
184             [&os](const auto& x) { os << x; },
185             [&os](const auto&) { os << "<unstreamable>"; }, x);
186       },
187       v);
188   return os;
189 }
190 
191 } // namespace axis
192 } // namespace histogram
193 } // namespace boost
194 
195 #endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
196 
197 #endif
198