/* Copyright 2024-2025 Joaquin M Lopez Munoz. * 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) * * See http://www.boost.org/libs/poly_collection for library home page. */ #ifndef BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP #define BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost{ namespace poly_collection{ namespace fixed_variant_impl{ /* fixed_variant behaves as a variant without the possibility to change the * initial alternative. The referenced value does not belong in fixed_variant * but assumed to be right before it, which layout is implemented by * fixed_variant_closure>. This approach allows us to pack * same-alternative fixed_variants optimally, without reserving space for * the largest alternative type. */ template class fixed_variant { static_assert( !mp11::mp_empty::value, "the variant can't be specified with zero types"); static_assert( mp11::mp_is_set::value, "all types in the variant must be distinct"); static constexpr std::size_t N=sizeof...(Ts); using index_type=mp11::mp_cond< mp11::mp_bool< (N<=(std::numeric_limits::max)())>,unsigned char, mp11::mp_bool< (N<=(std::numeric_limits::max)())>,unsigned short, mp11::mp_true, std::size_t >; public: template< typename T, std::size_t I=mp11::mp_find::value, typename std::enable_if< (I::value)>::type* =nullptr > explicit fixed_variant(const T&):index_{static_cast(I)}{} std::size_t index()const noexcept{return index_;} bool valueless_by_exception()const noexcept{return false;} #if BOOST_WORKAROUND(BOOST_MSVC,<1920) /* spurious C2248 when perfect forwarding fixed_variant */ #else protected: fixed_variant(const fixed_variant&)=default; fixed_variant& operator=(const fixed_variant&)=default; #endif private: index_type index_; }; template struct fixed_variant_store { template fixed_variant_store(Args&&... args):value{std::forward(args)...}{} T value; }; template class fixed_variant_closure:public fixed_variant_store,public Base { public: template< typename... Args, typename std::enable_if< std::is_constructible::value >::type* =nullptr > fixed_variant_closure(Args&&... args) noexcept(std::is_nothrow_constructible::value): fixed_variant_store{std::forward(args)...}, Base{this->value} {} fixed_variant_closure(const fixed_variant_closure&)=default; fixed_variant_closure(fixed_variant_closure&&)=default; fixed_variant_closure& operator=(fixed_variant_closure&&)=default; template< typename Q=T, typename std::enable_if< detail::is_equality_comparable::value,bool>::type* =nullptr > bool operator==(const fixed_variant_closure& x)const noexcept(detail::is_nothrow_equality_comparable::value) { return this->value==x.value; } }; struct bad_variant_access:std::exception { bad_variant_access()noexcept{} const char* what()const noexcept{return "bad variant access";} }; template struct variant_size; template struct variant_size>: std::integral_constant{}; template struct variant_size:variant_size{}; #ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES template constexpr std::size_t variant_size_v=variant_size::value; #endif template struct variant_alternative; template struct is_fixed_variant:std::false_type{}; template struct is_fixed_variant>:std::true_type{}; template struct transfer_cref{using type=Q;}; template struct transfer_cref{using type=const Q;}; template struct transfer_cref {using type=typename transfer_cref::type&;}; template struct transfer_cref {using type=typename transfer_cref::type&&;}; template struct variant_alternative< I,V, typename std::enable_if< is_fixed_variant::type>::value >::type > { using type=typename transfer_cref< V,mp11::mp_at_c::type,I>>::type; }; template using variant_alternative_t=typename variant_alternative::type; template bool holds_alternative(const fixed_variant& x)noexcept { static_assert( mp11::mp_contains,T>::value, "type must be one of the variant alternatives"); return x.index()==mp11::mp_find,T>::value; } template const T& unsafe_get(const fixed_variant& x) { return static_cast>&> (x).value; } template T& unsafe_get(fixed_variant& x) { return const_cast( unsafe_get(const_cast&>(x))); } template const T&& unsafe_get(const fixed_variant&& x) { return std::move(unsafe_get(x)); } template T&& unsafe_get(fixed_variant&& x) { return std::move(unsafe_get(x)); } template variant_alternative_t unsafe_get(V&& x) { using raw_variant=typename std::decay::type; return unsafe_get>(std::forward(x)); } template variant_alternative_t get(V&& x) { using raw_variant=typename std::decay::type; static_assert( I::value, "index must be less than the number of alternatives"); if(x.index()!=I)throw bad_variant_access{}; else return unsafe_get(std::forward(x)); } template auto get(V&& x)->decltype(unsafe_get(std::forward(x))) { using raw_variant=typename std::decay::type; static_assert( mp11::mp_contains::value, "type must be one of the variant alternatives"); if(!holds_alternative(x))throw bad_variant_access{}; else return unsafe_get(std::forward(x)); } template variant_alternative_t>* get_if(fixed_variant* px)noexcept { if(!px||px->index()!=I)return nullptr; else return std::addressof(unsafe_get(*px)); } template const variant_alternative_t>* get_if(const fixed_variant* px)noexcept { return get_if(const_cast*>(px)); } template T* get_if(fixed_variant* px)noexcept { if(!px||!holds_alternative(*px))return nullptr; else return std::addressof(unsafe_get(*px)); } template const T* get_if(const fixed_variant* px)noexcept { return get_if(const_cast*>(px)); } struct deduced; template struct return_type_impl { using type=R; }; template struct return_type_impl { using type=decltype(std::declval()(get<0>(std::declval())...)); }; template using return_type=typename return_type_impl::type; template struct visit_helper; template struct visit_helper { F&& f; R operator()(){return std::forward(f)();} }; template struct visit_helper { F&& f; void operator()(){(void)std::forward(f)();} }; template< typename R=deduced,typename F, typename ReturnType=return_type > ReturnType visit(F&& f) { return visit_helper{std::forward(f)}(); } template struct visit_helper { F&& f; V&& x; template R operator()(I) { return std::forward(f)(unsafe_get(std::forward(x))); } }; template struct visit_helper { F&& f; V&& x; template void operator()(I) { (void)std::forward(f)(unsafe_get(std::forward(x))); } }; template< typename R=deduced,typename F,typename V, typename ReturnType=return_type > ReturnType visit(F&& f,V&& x) { using raw_variant=typename std::decay::type; return mp11::mp_with_index::value>( x.index(), visit_helper{std::forward(f),std::forward(x)}); } template struct bound_f { F&& f; V&& x; template R operator()(Args&&... xs) { return std::forward(f)( unsafe_get(std::forward(x)),std::forward(xs)...); } }; template struct bound_f { F&& f; V&& x; template void operator()(Args&&... xs) { (void)std::forward(f)( unsafe_get(std::forward(x)),std::forward(xs)...); } }; template struct bound_visit; template struct visit_helper { F&& f; V&& x; std::tuple xs; template R operator()(I) { return mp11::tuple_apply( bound_visit>{{std::forward(f),std::forward(x)}}, std::move(xs)); } }; template< typename R=deduced,typename F,typename V1,typename V2,typename... Vs, typename ReturnType=return_type > ReturnType visit(F&& f,V1&& x1,V2&& x2,Vs&&... xs) { using raw_variant=typename std::decay::type; return mp11::mp_with_index::value>( x1.index(), visit_helper{ std::forward(f),std::forward(x1), std::forward_as_tuple(std::forward(x2),std::forward(xs)...)}); } template struct bound_visit { F&& f; template R operator()(Vs&&... xs) { return visit(std::forward(f),std::forward(xs)...); } }; template struct return_type_by_index_impl { using type=R; }; template struct return_type_by_index_impl { using type=decltype(std::declval()(get<0>(std::declval()))); }; template using return_type_by_index=typename return_type_by_index_impl::type; template struct visit_by_index_helper { V&& x; std::tuple fs; template R operator()(I) { return std::get(std::move(fs))( unsafe_get(std::forward(x))); } }; template struct visit_by_index_helper { V&& x; std::tuple fs; template void operator()(I) { (void)std::get(std::move(fs))( unsafe_get(std::forward(x))); } }; template< typename R=deduced,typename V,typename... Fs, typename ReturnType=return_type_by_index > ReturnType visit_by_index(V&& x,Fs&&... fs) { using raw_variant=typename std::decay::type; static_assert( mp11::mp_size::value==sizeof...(Fs), "the number of function objects must be the same as that of the " "alternative types in the variant"); return mp11::mp_with_index::value>( x.index(), visit_by_index_helper{ std::forward(x), std::forward_as_tuple(std::forward(fs)...)}); } template struct relop_helper { const fixed_variant &x,&y; template bool operator()(I)const { return RelOp{}(unsafe_get(x),unsafe_get(y)); } }; struct eq_ { template bool operator()(const T& x,const T& y)const{return x==y;} }; template bool operator==( const fixed_variant& x,const fixed_variant& y) { return x.index()==y.index()&& mp11::mp_with_index(x.index(),relop_helper{x,y}); } struct neq_ { template bool operator()(const T& x,const T& y)const{return x!=y;} }; template bool operator!=( const fixed_variant& x,const fixed_variant& y) { return x.index()!=y.index()|| mp11::mp_with_index( x.index(),relop_helper{x,y}); } struct lt_ { template bool operator()(const T& x,const T& y)const{return x bool operator<( const fixed_variant& x,const fixed_variant& y) { return x.index()( x.index(),relop_helper{x,y})); } struct lte_ { template bool operator()(const T& x,const T& y)const{return x<=y;} }; template bool operator<=( const fixed_variant& x,const fixed_variant& y) { return x.index()( x.index(),relop_helper{x,y})); } struct gt_ { template bool operator()(const T& x,const T& y)const{return x>y;} }; template bool operator>( const fixed_variant& x,const fixed_variant& y) { return x.index()>y.index()|| (x.index()==y.index()&&mp11::mp_with_index( x.index(),relop_helper{x,y})); } struct gte_ { template bool operator()(const T& x,const T& y)const{return x>=y;} }; template bool operator>=( const fixed_variant& x,const fixed_variant& y) { return x.index()>y.index()|| (x.index()==y.index()&&mp11::mp_with_index( x.index(),relop_helper{x,y})); } } /* namespace poly_collection::fixed_variant_impl */ using boost::poly_collection::fixed_variant_impl::variant_size; #ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES using boost::poly_collection::fixed_variant_impl::variant_size_v; #endif using boost::poly_collection::fixed_variant_impl::bad_variant_access; using boost::poly_collection::fixed_variant_impl::variant_alternative; using boost::poly_collection::fixed_variant_impl::variant_alternative_t; using boost::poly_collection::fixed_variant_impl::visit; using boost::poly_collection::fixed_variant_impl::visit_by_index; using boost::poly_collection::fixed_variant_impl::holds_alternative; using boost::poly_collection::fixed_variant_impl::get; using boost::poly_collection::fixed_variant_impl::unsafe_get; using boost::poly_collection::fixed_variant_impl::get_if; } /* namespace poly_collection */ } /* namespace boost */ #endif