// Copyright 2015-2017 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_VARIANT_HPP #define BOOST_HISTOGRAM_AXIS_VARIANT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace histogram { namespace axis { template T* get_if(variant* v); template const T* get_if(const variant* v); /// Polymorphic axis type template class variant : public iterator_mixin> { using impl_type = boost::variant; using raw_types = mp11::mp_transform; template using is_bounded_type = mp11::mp_contains>; template using requires_bounded_type = std::enable_if_t::value>; // maybe metadata_type or const metadata_type, if bounded type is const using metadata_type = std::remove_reference_t>()))>; public: // cannot import ctors with using directive, it breaks gcc and msvc variant() = default; variant(const variant&) = default; variant& operator=(const variant&) = default; variant(variant&&) = default; variant& operator=(variant&&) = default; template > variant(T&& t) : impl(std::forward(t)) {} template > variant& operator=(T&& t) { impl = std::forward(t); return *this; } template variant(const variant& u) { this->operator=(u); } template variant& operator=(const variant& u) { visit( [this](const auto& u) { using U = detail::remove_cvref_t; detail::static_if>( [this](const auto& u) { this->operator=(u); }, [](const auto&) { BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat( boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " is not convertible to a bounded type of ", boost::core::demangled_name(BOOST_CORE_TYPEID(variant))))); }, u); }, u); return *this; } /// Return size of axis. index_type size() const { return visit([](const auto& x) { return x.size(); }, *this); } /// Return options of axis or option::none_t if axis has no options. unsigned options() const { return visit([](const auto& x) { return axis::traits::options(x); }, *this); } /// Return reference to const metadata or instance of null_type if axis has no /// metadata. const metadata_type& metadata() const { return visit( [](const auto& a) -> const metadata_type& { using M = decltype(traits::metadata(a)); return detail::static_if>( [](const auto& a) -> const metadata_type& { return traits::metadata(a); }, [](const auto&) -> const metadata_type& { BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat( "cannot return metadata of type ", boost::core::demangled_name(BOOST_CORE_TYPEID(M)), " through axis::variant interface which uses type ", boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type)), "; use boost::histogram::axis::get to obtain a reference " "of this axis type"))); }, a); }, *this); } /// Return reference to metadata or instance of null_type if axis has no /// metadata. metadata_type& metadata() { return visit( [](auto&& a) -> metadata_type& { using M = decltype(traits::metadata(a)); return detail::static_if>( [](auto& a) -> metadata_type& { return traits::metadata(a); }, [](auto&) -> metadata_type& { BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat( "cannot return metadata of type ", boost::core::demangled_name(BOOST_CORE_TYPEID(M)), " through axis::variant interface which uses type ", boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type)), "; use boost::histogram::axis::get to obtain a reference " "of this axis type"))); }, a); }, *this); } /// Return index for value argument. /// Throws std::invalid_argument if axis has incompatible call signature. template index_type index(const U& u) const { return visit([&u](const auto& a) { return traits::index(a, u); }, *this); } /// Return value for index argument. /// Only works for axes with value method that returns something convertible /// to double and will throw a runtime_error otherwise, see /// axis::traits::value(). double value(real_index_type idx) const { return visit([idx](const auto& a) { return traits::value_as(a, idx); }, *this); } /// Return bin for index argument. /// Only works for axes with value method that returns something convertible /// to double and will throw a runtime_error otherwise, see /// axis::traits::value(). auto bin(index_type idx) const { return visit( [idx](const auto& a) { return detail::value_method_switch_with_return_type>( [idx](const auto& a) { // axis is discrete const auto x = a.value(idx); return polymorphic_bin(x, x); }, [idx](const auto& a) { // axis is continuous return polymorphic_bin(a.value(idx), a.value(idx + 1)); }, a); }, *this); } template bool operator==(const variant& u) const { return visit([&u](const auto& x) { return u == x; }, *this); } template bool operator==(const T& t) const { // boost::variant::operator==(T) implemented only to fail, cannot use it auto tp = get_if(this); return tp && detail::relaxed_equal(*tp, t); } template bool operator!=(const T& t) const { return !operator==(t); } template void serialize(Archive& ar, unsigned); template friend auto visit(Visitor&&, Variant &&) -> detail::visitor_return_type; template friend T& get(variant& v); template friend const T& get(const variant& v); template friend T&& get(variant&& v); template friend T* get_if(variant* v); template friend const T* get_if(const variant* v); private: boost::variant impl; }; /// Apply visitor to variant. template auto visit(Visitor&& vis, Variant&& var) -> detail::visitor_return_type { return boost::apply_visitor(std::forward(vis), var.impl); } /// Return lvalue reference to T, throws unspecified exception if type does not /// match. template T& get(variant& v) { return boost::get(v.impl); } /// Return rvalue reference to T, throws unspecified exception if type does not /// match. template T&& get(variant&& v) { return boost::get(std::move(v.impl)); } /// Return const reference to T, throws unspecified exception if type does not /// match. template const T& get(const variant& v) { return boost::get(v.impl); } /// Returns pointer to T in variant or null pointer if type does not match. template T* get_if(variant* v) { return boost::relaxed_get(&(v->impl)); } /// Returns pointer to const T in variant or null pointer if type does not /// match. template const T* get_if(const variant* v) { return boost::relaxed_get(&(v->impl)); } #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED // pass-through version of get for generic programming template decltype(auto) get(U&& u) { return static_cast>(u); } // pass-through version of get_if for generic programming template T* get_if(U* u) { return std::is_same>::value ? reinterpret_cast(u) : nullptr; } // pass-through version of get_if for generic programming template const T* get_if(const U* u) { return std::is_same>::value ? reinterpret_cast(u) : nullptr; } #endif } // namespace axis } // namespace histogram } // namespace boost #endif