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.
5 // Copyright (C) 2018-2019 Intel Corporation
8 #ifndef OPENCV_GAPI_UTIL_VARIANT_HPP
9 #define OPENCV_GAPI_UTIL_VARIANT_HPP
12 #include <type_traits>
14 #include "opencv2/gapi/util/throw.hpp"
15 #include "opencv2/gapi/util/util.hpp" // max_of_t
17 // A poor man's `variant` implementation, incompletely modeled against C++17 spec.
24 template<std::size_t I, typename Target, typename First, typename... Remaining>
25 struct type_list_index_helper
27 static const constexpr bool is_same = std::is_same<Target, First>::value;
28 static const constexpr std::size_t value =
29 std::conditional<is_same, std::integral_constant<std::size_t, I>, type_list_index_helper<I + 1, Target, Remaining...>>::type::value;
32 template<std::size_t I, typename Target, typename First>
33 struct type_list_index_helper<I, Target, First>
35 static_assert(std::is_same<Target, First>::value, "Type not found");
36 static const constexpr std::size_t value = I;
40 template<class T, class U, class V> using are_different =
41 std::enable_if<!std::is_same<typename std::decay<T>::type,
42 typename std::decay<U>::type>::value,
46 template<typename Target, typename... Types>
47 struct type_list_index
49 static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
52 class bad_variant_access: public std::exception
55 virtual const char *what() const noexcept override
57 return "Bad variant access";
61 // Interface ///////////////////////////////////////////////////////////////
63 inline bool operator==(const util::monostate&, const util::monostate&)
68 template<typename... Ts> // FIXME: no references, arrays, and void
71 // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped
72 static constexpr const std::size_t S = cv::detail::max_of_t<sizeof(Ts)...>::value;
73 static constexpr const std::size_t A = cv::detail::max_of_t<alignof(Ts)...>::value;
74 using Memory = typename std::aligned_storage<S, A>::type[1];
76 template<typename T> struct cctr_h {
77 static void help(Memory memory, const Memory from) {
78 new (memory) T(*reinterpret_cast<const T*>(from));
82 template<typename T> struct vctr_h {
83 static void help(Memory memory, const void* pval) {
84 new (memory) T(*reinterpret_cast<const T*>(pval));
88 template<typename T> struct mctr_h {
89 static void help(Memory memory, void *pval) {
90 new (memory) T(std::move(*reinterpret_cast<T*>(pval)));
94 template<typename T> struct copy_h {
95 static void help(Memory to, const Memory from) {
96 *reinterpret_cast<T*>(to) = *reinterpret_cast<const T*>(from);
100 template<typename T> struct move_h {
101 static void help(Memory to, const Memory from) {
102 *reinterpret_cast<T*>(to) = std::move(*reinterpret_cast<const T*>(from));
106 template<typename T> struct swap_h {
107 static void help(Memory to, Memory from) {
108 std::swap(*reinterpret_cast<T*>(to), *reinterpret_cast<T*>(from));
112 template<typename T> struct dtor_h {
113 static void help(Memory memory) {
114 (void) memory; // MSCV warning
115 reinterpret_cast<T*>(memory)->~T();
119 template<typename T> struct equal_h {
120 static bool help(const Memory lhs, const Memory rhs) {
121 const T& t_lhs = *reinterpret_cast<const T*>(lhs);
122 const T& t_rhs = *reinterpret_cast<const T*>(rhs);
123 return t_lhs == t_rhs;
127 typedef void (*CCtr) (Memory, const Memory); // Copy c-tor (variant)
128 typedef void (*VCtr) (Memory, const void*); // Copy c-tor (value)
129 typedef void (*MCtr) (Memory, void*); // Generic move c-tor
130 typedef void (*Copy) (Memory, const Memory); // Copy assignment
131 typedef void (*Move) (Memory, const Memory); // Move assignment
132 typedef void (*Swap) (Memory, Memory); // Swap
133 typedef void (*Dtor) (Memory); // Destructor
135 typedef bool (*Equal)(const Memory, const Memory); // Equality test (external)
137 static constexpr std::array<CCtr, sizeof...(Ts)> cctrs(){ return {{(&cctr_h<Ts>::help)...}};}
138 static constexpr std::array<VCtr, sizeof...(Ts)> vctrs(){ return {{(&vctr_h<Ts>::help)...}};}
139 static constexpr std::array<MCtr, sizeof...(Ts)> mctrs(){ return {{(&mctr_h<Ts>::help)...}};}
140 static constexpr std::array<Copy, sizeof...(Ts)> cpyrs(){ return {{(©_h<Ts>::help)...}};}
141 static constexpr std::array<Move, sizeof...(Ts)> mvers(){ return {{(&move_h<Ts>::help)...}};}
142 static constexpr std::array<Swap, sizeof...(Ts)> swprs(){ return {{(&swap_h<Ts>::help)...}};}
143 static constexpr std::array<Dtor, sizeof...(Ts)> dtors(){ return {{(&dtor_h<Ts>::help)...}};}
145 std::size_t m_index = 0;
148 template<typename T, typename... Us> friend T& get(variant<Us...> &v);
149 template<typename T, typename... Us> friend const T& get(const variant<Us...> &v);
150 template<typename... Us> friend bool operator==(const variant<Us...> &lhs,
151 const variant<Us...> &rhs);
157 variant(const variant& other);
158 variant(variant&& other) noexcept;
159 template<typename T> explicit variant(const T& t);
160 // are_different is a SFINAE trick to avoid variant(T &&t) with T=variant
161 // for some reason, this version is called instead of variant(variant&& o) when
162 // variant is used in STL containers (examples: vector assignment)
163 template<typename T> explicit variant(T&& t, typename detail::are_different<variant, T, int>::type = 0);
164 // template<class T, class... Args> explicit variant(Args&&... args);
165 // FIXME: other constructors
171 variant& operator=(const variant& rhs);
172 variant& operator=(variant &&rhs) noexcept;
174 // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above
176 typename detail::are_different<variant, T, variant&>
177 ::type operator=(T&& t) noexcept;
180 std::size_t index() const noexcept;
181 // FIXME: valueless_by_exception()
185 void swap(variant &rhs) noexcept;
188 template<typename T> static constexpr std::size_t index_of();
193 template<typename T, typename... Types>
194 T& get(util::variant<Types...> &v);
196 template<typename T, typename... Types>
197 const T& get(const util::variant<Types...> &v);
199 template<typename T, typename... Types>
200 bool holds_alternative(const util::variant<Types...> &v) noexcept;
202 // FIXME: T&&, const TT&& versions.
204 // Implementation //////////////////////////////////////////////////////////
205 template<typename... Ts>
206 variant<Ts...>::variant() noexcept
208 typedef typename std::tuple_element<0, std::tuple<Ts...> >::type TFirst;
209 new (memory) TFirst();
212 template<typename... Ts>
213 variant<Ts...>::variant(const variant &other)
214 : m_index(other.m_index)
216 (cctrs()[m_index])(memory, other.memory);
219 template<typename... Ts>
220 variant<Ts...>::variant(variant &&other) noexcept
221 : m_index(other.m_index)
223 (mctrs()[m_index])(memory, other.memory);
226 template<typename... Ts>
228 variant<Ts...>::variant(const T& t)
229 : m_index(util::type_list_index<T, Ts...>::value)
231 (vctrs()[m_index])(memory, &t);
234 template<typename... Ts>
236 variant<Ts...>::variant(T&& t, typename detail::are_different<variant, T, int>::type)
237 : m_index(util::type_list_index<typename std::remove_reference<T>::type, Ts...>::value)
239 (mctrs()[m_index])(memory, &t);
242 template<typename... Ts>
243 variant<Ts...>::~variant()
245 (dtors()[m_index])(memory);
248 template<typename... Ts>
249 variant<Ts...>& variant<Ts...>::operator=(const variant<Ts...> &rhs)
251 if (m_index != rhs.m_index)
253 (dtors()[ m_index])(memory);
254 (cctrs()[rhs.m_index])(memory, rhs.memory);
255 m_index = rhs.m_index;
259 (cpyrs()[rhs.m_index])(memory, rhs.memory);
264 template<typename... Ts>
265 variant<Ts...>& variant<Ts...>::operator=(variant<Ts...> &&rhs) noexcept
267 if (m_index != rhs.m_index)
269 (dtors()[ m_index])(memory);
270 (mctrs()[rhs.m_index])(memory, rhs.memory);
271 m_index = rhs.m_index;
275 (mvers()[rhs.m_index])(memory, rhs.memory);
280 template<typename... Ts>
281 template<class T> typename detail::are_different<variant<Ts...>, T, variant<Ts...>&>
282 ::type variant<Ts...>::operator=(T&& t) noexcept
284 // FIXME: No version with implicit type conversion available!
285 static const constexpr std::size_t t_index =
286 util::type_list_index<T, Ts...>::value;
288 if (t_index == m_index)
290 util::get<T>(*this) = std::move(t);
293 else return (*this = variant(std::move(t)));
296 template<typename... Ts>
297 std::size_t util::variant<Ts...>::index() const noexcept
302 template<typename... Ts>
303 void variant<Ts...>::swap(variant<Ts...> &rhs) noexcept
305 if (m_index == rhs.index())
307 (swprs()[m_index](memory, rhs.memory));
311 variant<Ts...> tmp(std::move(*this));
312 *this = std::move(rhs);
313 rhs = std::move(tmp);
317 template<typename... Ts>
319 constexpr std::size_t variant<Ts...>::index_of()
321 return util::type_list_index<T, Ts...>::value; // FIXME: tests!
324 template<typename T, typename... Types>
325 T& get(util::variant<Types...> &v)
327 const constexpr std::size_t t_index =
328 util::type_list_index<T, Types...>::value;
330 if (v.index() == t_index)
331 return reinterpret_cast<T&>(v.memory);
333 throw_error(bad_variant_access());
336 template<typename T, typename... Types>
337 const T& get(const util::variant<Types...> &v)
339 const constexpr std::size_t t_index =
340 util::type_list_index<T, Types...>::value;
342 if (v.index() == t_index)
343 return reinterpret_cast<const T&>(v.memory);
345 throw_error(bad_variant_access());
348 template<typename T, typename... Types>
349 bool holds_alternative(const util::variant<Types...> &v) noexcept
351 return v.index() == util::variant<Types...>::template index_of<T>();
354 template<typename... Us> bool operator==(const variant<Us...> &lhs,
355 const variant<Us...> &rhs)
357 using V = variant<Us...>;
359 // Instantiate table only here since it requires operator== for <Us...>
360 // <Us...> should have operator== only if this one is used, not in general
361 static const std::array<typename V::Equal, sizeof...(Us)> eqs = {
362 {(&V::template equal_h<Us>::help)...}
364 if (lhs.index() != rhs.index())
366 return (eqs[lhs.index()])(lhs.memory, rhs.memory);
369 template<typename... Us> bool operator!=(const variant<Us...> &lhs,
370 const variant<Us...> &rhs)
372 return !(lhs == rhs);
377 #endif // OPENCV_GAPI_UTIL_VARIANT_HPP