fixed_variant.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /* Copyright 2024-2025 Joaquin M Lopez Munoz.
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * See http://www.boost.org/libs/poly_collection for library home page.
  7. */
  8. #ifndef BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP
  9. #define BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP
  10. #if defined(_MSC_VER)
  11. #pragma once
  12. #endif
  13. #include <boost/config.hpp>
  14. #include <boost/core/addressof.hpp>
  15. #include <boost/detail/workaround.hpp>
  16. #include <boost/mp11/algorithm.hpp>
  17. #include <boost/mp11/function.hpp>
  18. #include <boost/mp11/list.hpp>
  19. #include <boost/mp11/set.hpp>
  20. #include <boost/mp11/tuple.hpp>
  21. #include <boost/mp11/utility.hpp>
  22. #include <boost/poly_collection/detail/is_equality_comparable.hpp>
  23. #include <boost/poly_collection/detail/is_nothrow_eq_comparable.hpp>
  24. #include <boost/type_traits/is_constructible.hpp>
  25. #include <cstddef>
  26. #include <limits>
  27. #include <memory>
  28. #include <stdexcept>
  29. #include <type_traits>
  30. #include <utility>
  31. namespace boost{
  32. namespace poly_collection{
  33. namespace fixed_variant_impl{
  34. /* fixed_variant behaves as a variant without the possibility to change the
  35. * initial alternative. The referenced value does not belong in fixed_variant
  36. * but assumed to be right before it, which layout is implemented by
  37. * fixed_variant_closure<T,fixed_variant<...>>. This approach allows us to pack
  38. * same-alternative fixed_variants optimally, without reserving space for
  39. * the largest alternative type.
  40. */
  41. template<typename... Ts>
  42. class fixed_variant
  43. {
  44. static_assert(
  45. !mp11::mp_empty<fixed_variant>::value,
  46. "the variant can't be specified with zero types");
  47. static_assert(
  48. mp11::mp_is_set<fixed_variant>::value,
  49. "all types in the variant must be distinct");
  50. static constexpr std::size_t N=sizeof...(Ts);
  51. using index_type=mp11::mp_cond<
  52. mp11::mp_bool<
  53. (N<=(std::numeric_limits<unsigned char>::max)())>,unsigned char,
  54. mp11::mp_bool<
  55. (N<=(std::numeric_limits<unsigned short>::max)())>,unsigned short,
  56. mp11::mp_true, std::size_t
  57. >;
  58. public:
  59. template<
  60. typename T,
  61. std::size_t I=mp11::mp_find<fixed_variant,T>::value,
  62. typename std::enable_if<
  63. (I<mp11::mp_size<fixed_variant>::value)>::type* =nullptr
  64. >
  65. explicit fixed_variant(const T&):index_{static_cast<index_type>(I)}{}
  66. std::size_t index()const noexcept{return index_;}
  67. bool valueless_by_exception()const noexcept{return false;}
  68. #if BOOST_WORKAROUND(BOOST_MSVC,<1920)
  69. /* spurious C2248 when perfect forwarding fixed_variant */
  70. #else
  71. protected:
  72. fixed_variant(const fixed_variant&)=default;
  73. fixed_variant& operator=(const fixed_variant&)=default;
  74. #endif
  75. private:
  76. index_type index_;
  77. };
  78. template<typename T>
  79. struct fixed_variant_store
  80. {
  81. template<typename... Args>
  82. fixed_variant_store(Args&&... args):value{std::forward<Args>(args)...}{}
  83. T value;
  84. };
  85. template<typename T,typename Base>
  86. class fixed_variant_closure:public fixed_variant_store<T>,public Base
  87. {
  88. public:
  89. template<
  90. typename... Args,
  91. typename std::enable_if<
  92. std::is_constructible<T,Args&&...>::value
  93. >::type* =nullptr
  94. >
  95. fixed_variant_closure(Args&&... args)
  96. noexcept(std::is_nothrow_constructible<T,Args&&...>::value):
  97. fixed_variant_store<T>{std::forward<Args>(args)...},
  98. Base{this->value}
  99. {}
  100. fixed_variant_closure(const fixed_variant_closure&)=default;
  101. fixed_variant_closure(fixed_variant_closure&&)=default;
  102. fixed_variant_closure& operator=(fixed_variant_closure&&)=default;
  103. template<
  104. typename Q=T,
  105. typename std::enable_if<
  106. detail::is_equality_comparable<Q>::value,bool>::type* =nullptr
  107. >
  108. bool operator==(const fixed_variant_closure& x)const
  109. noexcept(detail::is_nothrow_equality_comparable<T>::value)
  110. {
  111. return this->value==x.value;
  112. }
  113. };
  114. struct bad_variant_access:std::exception
  115. {
  116. bad_variant_access()noexcept{}
  117. const char* what()const noexcept{return "bad variant access";}
  118. };
  119. template<typename T> struct variant_size;
  120. template<typename... Ts>
  121. struct variant_size<fixed_variant<Ts...>>:
  122. std::integral_constant<std::size_t, sizeof...(Ts)>{};
  123. template<typename T> struct variant_size<const T>:variant_size<T>{};
  124. #ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES
  125. template<typename T>
  126. constexpr std::size_t variant_size_v=variant_size<T>::value;
  127. #endif
  128. template<std::size_t I,typename T,typename=void> struct variant_alternative;
  129. template<typename T> struct is_fixed_variant:std::false_type{};
  130. template<typename... Ts>
  131. struct is_fixed_variant<fixed_variant<Ts...>>:std::true_type{};
  132. template<typename T,typename Q>
  133. struct transfer_cref{using type=Q;};
  134. template<typename T,typename Q>
  135. struct transfer_cref<const T,Q>{using type=const Q;};
  136. template<typename T,typename Q>
  137. struct transfer_cref<T&,Q>
  138. {using type=typename transfer_cref<T,Q>::type&;};
  139. template<typename T,typename Q>
  140. struct transfer_cref<T&&,Q>
  141. {using type=typename transfer_cref<T,Q>::type&&;};
  142. template<std::size_t I,typename V>
  143. struct variant_alternative<
  144. I,V,
  145. typename std::enable_if<
  146. is_fixed_variant<typename std::decay<V>::type>::value
  147. >::type
  148. >
  149. {
  150. using type=typename transfer_cref<
  151. V,mp11::mp_at_c<typename std::decay<V>::type,I>>::type;
  152. };
  153. template<std::size_t I,typename V>
  154. using variant_alternative_t=typename variant_alternative<I,V>::type;
  155. template<typename T,typename... Ts>
  156. bool holds_alternative(const fixed_variant<Ts...>& x)noexcept
  157. {
  158. static_assert(
  159. mp11::mp_contains<fixed_variant<Ts...>,T>::value,
  160. "type must be one of the variant alternatives");
  161. return x.index()==mp11::mp_find<fixed_variant<Ts...>,T>::value;
  162. }
  163. template<typename T,typename... Ts>
  164. const T& unsafe_get(const fixed_variant<Ts...>& x)
  165. {
  166. return static_cast<const fixed_variant_closure<T,fixed_variant<Ts...>>&>
  167. (x).value;
  168. }
  169. template<typename T,typename... Ts>
  170. T& unsafe_get(fixed_variant<Ts...>& x)
  171. {
  172. return const_cast<T&>(
  173. unsafe_get<T>(const_cast<const fixed_variant<Ts...>&>(x)));
  174. }
  175. template<typename T,typename... Ts>
  176. const T&& unsafe_get(const fixed_variant<Ts...>&& x)
  177. {
  178. return std::move(unsafe_get<T>(x));
  179. }
  180. template<typename T,typename... Ts>
  181. T&& unsafe_get(fixed_variant<Ts...>&& x)
  182. {
  183. return std::move(unsafe_get<T>(x));
  184. }
  185. template<std::size_t I,typename V>
  186. variant_alternative_t<I,V&&> unsafe_get(V&& x)
  187. {
  188. using raw_variant=typename std::decay<V>::type;
  189. return unsafe_get<mp11::mp_at_c<raw_variant,I>>(std::forward<V>(x));
  190. }
  191. template<std::size_t I,typename V>
  192. variant_alternative_t<I,V&&> get(V&& x)
  193. {
  194. using raw_variant=typename std::decay<V>::type;
  195. static_assert(
  196. I<mp11::mp_size<raw_variant>::value,
  197. "index must be less than the number of alternatives");
  198. if(x.index()!=I)throw bad_variant_access{};
  199. else return unsafe_get<I>(std::forward<V>(x));
  200. }
  201. template<typename T,typename V>
  202. auto get(V&& x)->decltype(unsafe_get<T>(std::forward<V>(x)))
  203. {
  204. using raw_variant=typename std::decay<V>::type;
  205. static_assert(
  206. mp11::mp_contains<raw_variant,T>::value,
  207. "type must be one of the variant alternatives");
  208. if(!holds_alternative<T>(x))throw bad_variant_access{};
  209. else return unsafe_get<T>(std::forward<V>(x));
  210. }
  211. template<std::size_t I,typename... Ts>
  212. variant_alternative_t<I,fixed_variant<Ts...>>*
  213. get_if(fixed_variant<Ts...>* px)noexcept
  214. {
  215. if(!px||px->index()!=I)return nullptr;
  216. else return std::addressof(unsafe_get<I>(*px));
  217. }
  218. template<std::size_t I,typename... Ts>
  219. const variant_alternative_t<I,fixed_variant<Ts...>>*
  220. get_if(const fixed_variant<Ts...>* px)noexcept
  221. {
  222. return get_if<I>(const_cast<fixed_variant<Ts...>*>(px));
  223. }
  224. template<typename T,typename... Ts>
  225. T* get_if(fixed_variant<Ts...>* px)noexcept
  226. {
  227. if(!px||!holds_alternative<T>(*px))return nullptr;
  228. else return std::addressof(unsafe_get<T>(*px));
  229. }
  230. template<typename T,typename... Ts>
  231. const T* get_if(const fixed_variant<Ts...>* px)noexcept
  232. {
  233. return get_if<T>(const_cast<fixed_variant<Ts...>*>(px));
  234. }
  235. struct deduced;
  236. template<typename R,typename F,typename... Vs>
  237. struct return_type_impl
  238. {
  239. using type=R;
  240. };
  241. template<typename F,typename... Vs>
  242. struct return_type_impl<deduced,F,Vs...>
  243. {
  244. using type=decltype(std::declval<F>()(get<0>(std::declval<Vs>())...));
  245. };
  246. template<typename R,typename F,typename... Vs>
  247. using return_type=typename return_type_impl<R,F,Vs...>::type;
  248. template<typename R,typename F,typename... Vs>
  249. struct visit_helper;
  250. template<typename R,typename F>
  251. struct visit_helper<R,F>
  252. {
  253. F&& f;
  254. R operator()(){return std::forward<F>(f)();}
  255. };
  256. template<typename F>
  257. struct visit_helper<void,F>
  258. {
  259. F&& f;
  260. void operator()(){(void)std::forward<F>(f)();}
  261. };
  262. template<
  263. typename R=deduced,typename F,
  264. typename ReturnType=return_type<R,F&&>
  265. >
  266. ReturnType visit(F&& f)
  267. {
  268. return visit_helper<ReturnType,F>{std::forward<F>(f)}();
  269. }
  270. template<typename R,typename F,typename V>
  271. struct visit_helper<R,F,V>
  272. {
  273. F&& f;
  274. V&& x;
  275. template<typename I>
  276. R operator()(I)
  277. {
  278. return std::forward<F>(f)(unsafe_get<I::value>(std::forward<V>(x)));
  279. }
  280. };
  281. template<typename F,typename V>
  282. struct visit_helper<void,F,V>
  283. {
  284. F&& f;
  285. V&& x;
  286. template<typename I>
  287. void operator()(I)
  288. {
  289. (void)std::forward<F>(f)(unsafe_get<I::value>(std::forward<V>(x)));
  290. }
  291. };
  292. template<
  293. typename R=deduced,typename F,typename V,
  294. typename ReturnType=return_type<R,F&&,V&&>
  295. >
  296. ReturnType visit(F&& f,V&& x)
  297. {
  298. using raw_variant=typename std::decay<V>::type;
  299. return mp11::mp_with_index<mp11::mp_size<raw_variant>::value>(
  300. x.index(),
  301. visit_helper<ReturnType,F,V>{std::forward<F>(f),std::forward<V>(x)});
  302. }
  303. template<typename R,typename F,typename V,typename I>
  304. struct bound_f
  305. {
  306. F&& f;
  307. V&& x;
  308. template<typename... Args>
  309. R operator()(Args&&... xs)
  310. {
  311. return std::forward<F>(f)(
  312. unsafe_get<I::value>(std::forward<V>(x)),std::forward<Args>(xs)...);
  313. }
  314. };
  315. template<typename F,typename V,typename I>
  316. struct bound_f<void,F,V,I>
  317. {
  318. F&& f;
  319. V&& x;
  320. template<typename... Args>
  321. void operator()(Args&&... xs)
  322. {
  323. (void)std::forward<F>(f)(
  324. unsafe_get<I::value>(std::forward<V>(x)),std::forward<Args>(xs)...);
  325. }
  326. };
  327. template<typename R,typename F>
  328. struct bound_visit;
  329. template<typename R,typename F,typename V,typename... Vs>
  330. struct visit_helper<R,F,V,Vs...>
  331. {
  332. F&& f;
  333. V&& x;
  334. std::tuple<Vs&&...> xs;
  335. template<typename I>
  336. R operator()(I)
  337. {
  338. return mp11::tuple_apply(
  339. bound_visit<R,bound_f<R,F,V,I>>{{std::forward<F>(f),std::forward<V>(x)}},
  340. std::move(xs));
  341. }
  342. };
  343. template<
  344. typename R=deduced,typename F,typename V1,typename V2,typename... Vs,
  345. typename ReturnType=return_type<R,F&&,V1&&,V2&&,Vs&&...>
  346. >
  347. ReturnType visit(F&& f,V1&& x1,V2&& x2,Vs&&... xs)
  348. {
  349. using raw_variant=typename std::decay<V1>::type;
  350. return mp11::mp_with_index<mp11::mp_size<raw_variant>::value>(
  351. x1.index(),
  352. visit_helper<ReturnType,F,V1,V2,Vs...>{
  353. std::forward<F>(f),std::forward<V1>(x1),
  354. std::forward_as_tuple(std::forward<V2>(x2),std::forward<Vs>(xs)...)});
  355. }
  356. template<typename R,typename F>
  357. struct bound_visit
  358. {
  359. F&& f;
  360. template<typename... Vs>
  361. R operator()(Vs&&... xs)
  362. {
  363. return visit<R>(std::forward<F>(f),std::forward<Vs>(xs)...);
  364. }
  365. };
  366. template<typename R,typename V,typename... Fs>
  367. struct return_type_by_index_impl
  368. {
  369. using type=R;
  370. };
  371. template<typename V,typename F,typename... Fs>
  372. struct return_type_by_index_impl<deduced,V,F,Fs...>
  373. {
  374. using type=decltype(std::declval<F>()(get<0>(std::declval<V>())));
  375. };
  376. template<typename R,typename V,typename... Fs>
  377. using return_type_by_index=typename return_type_by_index_impl<R,V,Fs...>::type;
  378. template<typename R,typename V,typename... Fs>
  379. struct visit_by_index_helper
  380. {
  381. V&& x;
  382. std::tuple<Fs&&...> fs;
  383. template<typename I>
  384. R operator()(I)
  385. {
  386. return std::get<I::value>(std::move(fs))(
  387. unsafe_get<I::value>(std::forward<V>(x)));
  388. }
  389. };
  390. template<typename V,typename... Fs>
  391. struct visit_by_index_helper<void,V,Fs...>
  392. {
  393. V&& x;
  394. std::tuple<Fs&&...> fs;
  395. template<typename I>
  396. void operator()(I)
  397. {
  398. (void)std::get<I::value>(std::move(fs))(
  399. unsafe_get<I::value>(std::forward<V>(x)));
  400. }
  401. };
  402. template<
  403. typename R=deduced,typename V,typename... Fs,
  404. typename ReturnType=return_type_by_index<R,V&&,Fs&&...>
  405. >
  406. ReturnType visit_by_index(V&& x,Fs&&... fs)
  407. {
  408. using raw_variant=typename std::decay<V>::type;
  409. static_assert(
  410. mp11::mp_size<raw_variant>::value==sizeof...(Fs),
  411. "the number of function objects must be the same as that of the "
  412. "alternative types in the variant");
  413. return mp11::mp_with_index<mp11::mp_size<raw_variant>::value>(
  414. x.index(),
  415. visit_by_index_helper<ReturnType,V,Fs...>{
  416. std::forward<V>(x),
  417. std::forward_as_tuple(std::forward<Fs>(fs)...)});
  418. }
  419. template<typename RelOp,typename ... Ts>
  420. struct relop_helper
  421. {
  422. const fixed_variant<Ts...> &x,&y;
  423. template<typename I> bool operator()(I)const
  424. {
  425. return RelOp{}(unsafe_get<I::value>(x),unsafe_get<I::value>(y));
  426. }
  427. };
  428. struct eq_
  429. {
  430. template<typename T>
  431. bool operator()(const T& x,const T& y)const{return x==y;}
  432. };
  433. template<typename... Ts>
  434. bool operator==(
  435. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  436. {
  437. return
  438. x.index()==y.index()&&
  439. mp11::mp_with_index<sizeof...(Ts)>(x.index(),relop_helper<eq_,Ts...>{x,y});
  440. }
  441. struct neq_
  442. {
  443. template<typename T>
  444. bool operator()(const T& x,const T& y)const{return x!=y;}
  445. };
  446. template<typename... Ts>
  447. bool operator!=(
  448. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  449. {
  450. return
  451. x.index()!=y.index()||
  452. mp11::mp_with_index<sizeof...(Ts)>(
  453. x.index(),relop_helper<neq_,Ts...>{x,y});
  454. }
  455. struct lt_
  456. {
  457. template<typename T>
  458. bool operator()(const T& x,const T& y)const{return x<y;}
  459. };
  460. template<typename... Ts>
  461. bool operator<(
  462. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  463. {
  464. return
  465. x.index()<y.index()||
  466. (x.index()==y.index()&&mp11::mp_with_index<sizeof...(Ts)>(
  467. x.index(),relop_helper<lt_,Ts...>{x,y}));
  468. }
  469. struct lte_
  470. {
  471. template<typename T>
  472. bool operator()(const T& x,const T& y)const{return x<=y;}
  473. };
  474. template<typename... Ts>
  475. bool operator<=(
  476. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  477. {
  478. return
  479. x.index()<y.index()||
  480. (x.index()==y.index()&&mp11::mp_with_index<sizeof...(Ts)>(
  481. x.index(),relop_helper<lte_,Ts...>{x,y}));
  482. }
  483. struct gt_
  484. {
  485. template<typename T>
  486. bool operator()(const T& x,const T& y)const{return x>y;}
  487. };
  488. template<typename... Ts>
  489. bool operator>(
  490. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  491. {
  492. return
  493. x.index()>y.index()||
  494. (x.index()==y.index()&&mp11::mp_with_index<sizeof...(Ts)>(
  495. x.index(),relop_helper<gt_,Ts...>{x,y}));
  496. }
  497. struct gte_
  498. {
  499. template<typename T>
  500. bool operator()(const T& x,const T& y)const{return x>=y;}
  501. };
  502. template<typename... Ts>
  503. bool operator>=(
  504. const fixed_variant<Ts...>& x,const fixed_variant<Ts...>& y)
  505. {
  506. return
  507. x.index()>y.index()||
  508. (x.index()==y.index()&&mp11::mp_with_index<sizeof...(Ts)>(
  509. x.index(),relop_helper<gte_,Ts...>{x,y}));
  510. }
  511. } /* namespace poly_collection::fixed_variant_impl */
  512. using boost::poly_collection::fixed_variant_impl::variant_size;
  513. #ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES
  514. using boost::poly_collection::fixed_variant_impl::variant_size_v;
  515. #endif
  516. using boost::poly_collection::fixed_variant_impl::bad_variant_access;
  517. using boost::poly_collection::fixed_variant_impl::variant_alternative;
  518. using boost::poly_collection::fixed_variant_impl::variant_alternative_t;
  519. using boost::poly_collection::fixed_variant_impl::visit;
  520. using boost::poly_collection::fixed_variant_impl::visit_by_index;
  521. using boost::poly_collection::fixed_variant_impl::holds_alternative;
  522. using boost::poly_collection::fixed_variant_impl::get;
  523. using boost::poly_collection::fixed_variant_impl::unsafe_get;
  524. using boost::poly_collection::fixed_variant_impl::get_if;
  525. } /* namespace poly_collection */
  526. } /* namespace boost */
  527. #endif