variant.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. // Copyright (C) 2018 Intel Corporation
  6. #ifndef OPENCV_GAPI_UTIL_VARIANT_HPP
  7. #define OPENCV_GAPI_UTIL_VARIANT_HPP
  8. #include <array>
  9. #include <type_traits>
  10. #include "opencv2/gapi/util/throw.hpp"
  11. #include "opencv2/gapi/util/util.hpp" // max_of_t
  12. // A poor man's `variant` implementation, incompletely modeled against C++17 spec.
  13. namespace cv
  14. {
  15. namespace util
  16. {
  17. namespace detail
  18. {
  19. template<std::size_t I, typename Target, typename First, typename... Remaining>
  20. struct type_list_index_helper
  21. {
  22. static const constexpr bool is_same = std::is_same<Target, First>::value;
  23. static const constexpr std::size_t value =
  24. std::conditional<is_same, std::integral_constant<std::size_t, I>, type_list_index_helper<I + 1, Target, Remaining...>>::type::value;
  25. };
  26. template<std::size_t I, typename Target, typename First>
  27. struct type_list_index_helper<I, Target, First>
  28. {
  29. static_assert(std::is_same<Target, First>::value, "Type not found");
  30. static const constexpr std::size_t value = I;
  31. };
  32. template<class T, class U, class V> using are_different =
  33. std::enable_if<!std::is_same<typename std::decay<T>::type,
  34. typename std::decay<U>::type>::value,
  35. V>;
  36. }
  37. template<typename Target, typename... Types>
  38. struct type_list_index
  39. {
  40. static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
  41. };
  42. class bad_variant_access: public std::exception
  43. {
  44. public:
  45. virtual const char *what() const noexcept override
  46. {
  47. return "Bad variant access";
  48. }
  49. };
  50. // Interface ///////////////////////////////////////////////////////////////
  51. struct monostate {};
  52. inline bool operator==(const util::monostate&, const util::monostate&)
  53. {
  54. return true;
  55. }
  56. template<typename... Ts> // FIXME: no references, arrays, and void
  57. class variant
  58. {
  59. // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped
  60. static constexpr const std::size_t S = cv::detail::max_of_t<sizeof(Ts)...>::value;
  61. static constexpr const std::size_t A = cv::detail::max_of_t<alignof(Ts)...>::value;
  62. using Memory = typename std::aligned_storage<S, A>::type[1];
  63. template<typename T> struct cctr_h {
  64. static void help(Memory memory, const Memory from) {
  65. new (memory) T(*reinterpret_cast<const T*>(from));
  66. }
  67. };
  68. template<typename T> struct vctr_h {
  69. static void help(Memory memory, const void* pval) {
  70. new (memory) T(*reinterpret_cast<const T*>(pval));
  71. }
  72. };
  73. template<typename T> struct mctr_h {
  74. static void help(Memory memory, void *pval) {
  75. new (memory) T(std::move(*reinterpret_cast<T*>(pval)));
  76. }
  77. };
  78. template<typename T> struct copy_h {
  79. static void help(Memory to, const Memory from) {
  80. *reinterpret_cast<T*>(to) = *reinterpret_cast<const T*>(from);
  81. }
  82. };
  83. template<typename T> struct move_h {
  84. static void help(Memory to, const Memory from) {
  85. *reinterpret_cast<T*>(to) = std::move(*reinterpret_cast<const T*>(from));
  86. }
  87. };
  88. template<typename T> struct swap_h {
  89. static void help(Memory to, Memory from) {
  90. std::swap(*reinterpret_cast<T*>(to), *reinterpret_cast<T*>(from));
  91. }
  92. };
  93. template<typename T> struct dtor_h {
  94. static void help(Memory memory) {
  95. (void) memory; // MSCV warning
  96. reinterpret_cast<T*>(memory)->~T();
  97. }
  98. };
  99. template<typename T> struct equal_h {
  100. static bool help(const Memory lhs, const Memory rhs) {
  101. const T& t_lhs = *reinterpret_cast<const T*>(lhs);
  102. const T& t_rhs = *reinterpret_cast<const T*>(rhs);
  103. return t_lhs == t_rhs;
  104. }
  105. };
  106. typedef void (*CCtr) (Memory, const Memory); // Copy c-tor (variant)
  107. typedef void (*VCtr) (Memory, const void*); // Copy c-tor (value)
  108. typedef void (*MCtr) (Memory, void*); // Generic move c-tor
  109. typedef void (*Copy) (Memory, const Memory); // Copy assignment
  110. typedef void (*Move) (Memory, const Memory); // Move assignment
  111. typedef void (*Swap) (Memory, Memory); // Swap
  112. typedef void (*Dtor) (Memory); // Destructor
  113. typedef bool (*Equal)(const Memory, const Memory); // Equality test (external)
  114. static constexpr std::array<CCtr, sizeof...(Ts)> cctrs(){ return {{(&cctr_h<Ts>::help)...}};}
  115. static constexpr std::array<VCtr, sizeof...(Ts)> vctrs(){ return {{(&vctr_h<Ts>::help)...}};}
  116. static constexpr std::array<MCtr, sizeof...(Ts)> mctrs(){ return {{(&mctr_h<Ts>::help)...}};}
  117. static constexpr std::array<Copy, sizeof...(Ts)> cpyrs(){ return {{(&copy_h<Ts>::help)...}};}
  118. static constexpr std::array<Move, sizeof...(Ts)> mvers(){ return {{(&move_h<Ts>::help)...}};}
  119. static constexpr std::array<Swap, sizeof...(Ts)> swprs(){ return {{(&swap_h<Ts>::help)...}};}
  120. static constexpr std::array<Dtor, sizeof...(Ts)> dtors(){ return {{(&dtor_h<Ts>::help)...}};}
  121. std::size_t m_index = 0;
  122. protected:
  123. template<typename T, typename... Us> friend T& get(variant<Us...> &v);
  124. template<typename T, typename... Us> friend const T& get(const variant<Us...> &v);
  125. template<typename... Us> friend bool operator==(const variant<Us...> &lhs,
  126. const variant<Us...> &rhs);
  127. Memory memory;
  128. public:
  129. // Constructors
  130. variant() noexcept;
  131. variant(const variant& other);
  132. variant(variant&& other) noexcept;
  133. template<typename T> explicit variant(const T& t);
  134. // are_different is a SFINAE trick to avoid variant(T &&t) with T=variant
  135. // for some reason, this version is called instead of variant(variant&& o) when
  136. // variant is used in STL containers (examples: vector assignment)
  137. template<typename T> explicit variant(T&& t, typename detail::are_different<variant, T, int>::type = 0);
  138. // template<class T, class... Args> explicit variant(Args&&... args);
  139. // FIXME: other constructors
  140. // Destructor
  141. ~variant();
  142. // Assignment
  143. variant& operator=(const variant& rhs);
  144. variant& operator=(variant &&rhs) noexcept;
  145. // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above
  146. template<class T>
  147. typename detail::are_different<variant, T, variant&>
  148. ::type operator=(T&& t) noexcept;
  149. // Observers
  150. std::size_t index() const noexcept;
  151. // FIXME: valueless_by_exception()
  152. // Modifiers
  153. // FIXME: emplace()
  154. void swap(variant &rhs) noexcept;
  155. // Non-C++17x!
  156. template<typename T> static constexpr std::size_t index_of();
  157. };
  158. // FIMXE: visit
  159. template<typename T, typename... Types>
  160. T& get(util::variant<Types...> &v);
  161. template<typename T, typename... Types>
  162. const T& get(const util::variant<Types...> &v);
  163. template<typename T, typename... Types>
  164. bool holds_alternative(const util::variant<Types...> &v) noexcept;
  165. // FIXME: T&&, const TT&& versions.
  166. // Implementation //////////////////////////////////////////////////////////
  167. template<typename... Ts>
  168. variant<Ts...>::variant() noexcept
  169. {
  170. typedef typename std::tuple_element<0, std::tuple<Ts...> >::type TFirst;
  171. new (memory) TFirst();
  172. }
  173. template<typename... Ts>
  174. variant<Ts...>::variant(const variant &other)
  175. : m_index(other.m_index)
  176. {
  177. (cctrs()[m_index])(memory, other.memory);
  178. }
  179. template<typename... Ts>
  180. variant<Ts...>::variant(variant &&other) noexcept
  181. : m_index(other.m_index)
  182. {
  183. (mctrs()[m_index])(memory, other.memory);
  184. }
  185. template<typename... Ts>
  186. template<class T>
  187. variant<Ts...>::variant(const T& t)
  188. : m_index(util::type_list_index<T, Ts...>::value)
  189. {
  190. (vctrs()[m_index])(memory, &t);
  191. }
  192. template<typename... Ts>
  193. template<class T>
  194. variant<Ts...>::variant(T&& t, typename detail::are_different<variant, T, int>::type)
  195. : m_index(util::type_list_index<typename std::remove_reference<T>::type, Ts...>::value)
  196. {
  197. (mctrs()[m_index])(memory, &t);
  198. }
  199. template<typename... Ts>
  200. variant<Ts...>::~variant()
  201. {
  202. (dtors()[m_index])(memory);
  203. }
  204. template<typename... Ts>
  205. variant<Ts...>& variant<Ts...>::operator=(const variant<Ts...> &rhs)
  206. {
  207. if (m_index != rhs.m_index)
  208. {
  209. (dtors()[ m_index])(memory);
  210. (cctrs()[rhs.m_index])(memory, rhs.memory);
  211. m_index = rhs.m_index;
  212. }
  213. else
  214. {
  215. (cpyrs()[rhs.m_index])(memory, rhs.memory);
  216. }
  217. return *this;
  218. }
  219. template<typename... Ts>
  220. variant<Ts...>& variant<Ts...>::operator=(variant<Ts...> &&rhs) noexcept
  221. {
  222. if (m_index != rhs.m_index)
  223. {
  224. (dtors()[ m_index])(memory);
  225. (mctrs()[rhs.m_index])(memory, rhs.memory);
  226. m_index = rhs.m_index;
  227. }
  228. else
  229. {
  230. (mvers()[rhs.m_index])(memory, rhs.memory);
  231. }
  232. return *this;
  233. }
  234. template<typename... Ts>
  235. template<class T> typename detail::are_different<variant<Ts...>, T, variant<Ts...>&>
  236. ::type variant<Ts...>::operator=(T&& t) noexcept
  237. {
  238. // FIXME: No version with implicit type conversion available!
  239. static const constexpr std::size_t t_index =
  240. util::type_list_index<T, Ts...>::value;
  241. if (t_index == m_index)
  242. {
  243. util::get<T>(*this) = std::move(t);
  244. return *this;
  245. }
  246. else return (*this = variant(std::move(t)));
  247. }
  248. template<typename... Ts>
  249. std::size_t util::variant<Ts...>::index() const noexcept
  250. {
  251. return m_index;
  252. }
  253. template<typename... Ts>
  254. void variant<Ts...>::swap(variant<Ts...> &rhs) noexcept
  255. {
  256. if (m_index == rhs.index())
  257. {
  258. (swprs()[m_index](memory, rhs.memory));
  259. }
  260. else
  261. {
  262. variant<Ts...> tmp(std::move(*this));
  263. *this = std::move(rhs);
  264. rhs = std::move(tmp);
  265. }
  266. }
  267. template<typename... Ts>
  268. template<typename T>
  269. constexpr std::size_t variant<Ts...>::index_of()
  270. {
  271. return util::type_list_index<T, Ts...>::value; // FIXME: tests!
  272. }
  273. template<typename T, typename... Types>
  274. T& get(util::variant<Types...> &v)
  275. {
  276. const constexpr std::size_t t_index =
  277. util::type_list_index<T, Types...>::value;
  278. if (v.index() == t_index)
  279. return *(T*)(&v.memory); // workaround for ICC 2019
  280. // original code: return reinterpret_cast<T&>(v.memory);
  281. else
  282. throw_error(bad_variant_access());
  283. }
  284. template<typename T, typename... Types>
  285. const T& get(const util::variant<Types...> &v)
  286. {
  287. const constexpr std::size_t t_index =
  288. util::type_list_index<T, Types...>::value;
  289. if (v.index() == t_index)
  290. return *(const T*)(&v.memory); // workaround for ICC 2019
  291. // original code: return reinterpret_cast<const T&>(v.memory);
  292. else
  293. throw_error(bad_variant_access());
  294. }
  295. template<typename T, typename... Types>
  296. bool holds_alternative(const util::variant<Types...> &v) noexcept
  297. {
  298. return v.index() == util::variant<Types...>::template index_of<T>();
  299. }
  300. template<typename... Us> bool operator==(const variant<Us...> &lhs,
  301. const variant<Us...> &rhs)
  302. {
  303. using V = variant<Us...>;
  304. // Instantiate table only here since it requires operator== for <Us...>
  305. // <Us...> should have operator== only if this one is used, not in general
  306. static const std::array<typename V::Equal, sizeof...(Us)> eqs = {
  307. {(&V::template equal_h<Us>::help)...}
  308. };
  309. if (lhs.index() != rhs.index())
  310. return false;
  311. return (eqs[lhs.index()])(lhs.memory, rhs.memory);
  312. }
  313. template<typename... Us> bool operator!=(const variant<Us...> &lhs,
  314. const variant<Us...> &rhs)
  315. {
  316. return !(lhs == rhs);
  317. }
  318. } // namespace cv
  319. } // namespace util
  320. #endif // OPENCV_GAPI_UTIL_VARIANT_HPP