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 
7 
8 #ifndef OPENCV_GAPI_UTIL_VARIANT_HPP
9 #define OPENCV_GAPI_UTIL_VARIANT_HPP
10 
11 #include <array>
12 #include <type_traits>
13 
14 #include <opencv2/gapi/util/throw.hpp>
15 #include <opencv2/gapi/util/util.hpp> // max_of_t
16 #include <opencv2/gapi/util/type_traits.hpp>
17 
18 // A poor man's `variant` implementation, incompletely modeled against C++17 spec.
19 namespace cv
20 {
21 namespace util
22 {
23     namespace detail
24     {
25         template<std::size_t I, typename Target, typename First, typename... Remaining>
26         struct type_list_index_helper
27         {
28             static const constexpr bool is_same = std::is_same<Target, First>::value;
29             static const constexpr std::size_t value =
30                 std::conditional<is_same, std::integral_constant<std::size_t, I>, type_list_index_helper<I + 1, Target, Remaining...>>::type::value;
31         };
32 
33         template<std::size_t I, typename Target, typename First>
34         struct type_list_index_helper<I, Target, First>
35         {
36             static_assert(std::is_same<Target, First>::value, "Type not found");
37             static const constexpr std::size_t value = I;
38         };
39     }
40 
41     template<typename Target, typename... Types>
42     struct type_list_index
43     {
44         static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
45     };
46 
47     class bad_variant_access: public std::exception
48     {
49     public:
what() const50         virtual const char *what() const noexcept override
51         {
52             return "Bad variant access";
53         }
54     };
55 
56     // Interface ///////////////////////////////////////////////////////////////
57     struct monostate {};
operator ==(const util::monostate &,const util::monostate &)58     inline bool operator==(const util::monostate&, const util::monostate&)
59     {
60         return true;
61     }
62 
63     template<typename... Ts> // FIXME: no references, arrays, and void
64     class variant
65     {
66         // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped
67         static constexpr const std::size_t S = cv::detail::max_of_t<sizeof(Ts)...>::value;
68         static constexpr const std::size_t A = cv::detail::max_of_t<alignof(Ts)...>::value;
69         using Memory = typename std::aligned_storage<S, A>::type[1];
70 
71         template<typename T> struct cctr_h {
helpcv::util::variant::cctr_h72             static void help(Memory memory, const Memory from) {
73                 new (memory) T(*reinterpret_cast<const T*>(from));
74             }
75         };
76 
77         template<typename T> struct mctr_h {
helpcv::util::variant::mctr_h78             static void help(Memory memory, void *pval) {
79                 new (memory) T(std::move(*reinterpret_cast<T*>(pval)));
80             }
81         };
82 
83         //FIXME: unify with cctr_h and mctr_h
84         template<typename T> struct cnvrt_ctor_h {
helpcv::util::variant::cnvrt_ctor_h85             static void help(Memory memory, void* from) {
86                 using util::decay_t;
87                 new (memory) decay_t<T>(std::forward<T>(*reinterpret_cast<decay_t<T>*>(from)));
88             }
89         };
90 
91         template<typename T> struct copy_h {
helpcv::util::variant::copy_h92             static void help(Memory to, const Memory from) {
93                 *reinterpret_cast<T*>(to) = *reinterpret_cast<const T*>(from);
94             }
95         };
96 
97         template<typename T> struct move_h {
helpcv::util::variant::move_h98             static void help(Memory to, Memory from) {
99                 *reinterpret_cast<T*>(to) = std::move(*reinterpret_cast<T*>(from));
100             }
101         };
102 
103         //FIXME: unify with copy_h and move_h
104         template<typename T> struct cnvrt_assign_h {
helpcv::util::variant::cnvrt_assign_h105             static void help(Memory to, void* from) {
106                 using util::decay_t;
107                 *reinterpret_cast<decay_t<T>*>(to) = std::forward<T>(*reinterpret_cast<decay_t<T>*>(from));
108             }
109         };
110 
111         template<typename T> struct swap_h {
helpcv::util::variant::swap_h112             static void help(Memory to, Memory from) {
113                 std::swap(*reinterpret_cast<T*>(to), *reinterpret_cast<T*>(from));
114             }
115         };
116 
117         template<typename T> struct dtor_h {
helpcv::util::variant::dtor_h118             static void help(Memory memory) {
119                 (void) memory; // MSCV warning
120                 reinterpret_cast<T*>(memory)->~T();
121             }
122         };
123 
124         template<typename T> struct equal_h {
helpcv::util::variant::equal_h125             static bool help(const Memory lhs, const Memory rhs) {
126                 const T& t_lhs = *reinterpret_cast<const T*>(lhs);
127                 const T& t_rhs = *reinterpret_cast<const T*>(rhs);
128                 return t_lhs == t_rhs;
129             }
130         };
131 
132         typedef void (*CCtr) (Memory, const Memory);  // Copy c-tor (variant)
133         typedef void (*MCtr) (Memory, void*);         // Generic move c-tor
134         typedef void (*Copy) (Memory, const Memory);  // Copy assignment
135         typedef void (*Move) (Memory, Memory);        // Move assignment
136 
137         typedef void (*Swap) (Memory, Memory);        // Swap
138         typedef void (*Dtor) (Memory);                // Destructor
139 
140         using  cnvrt_assgn_t   = void (*) (Memory, void*);  // Converting assignment (via std::forward)
141         using  cnvrt_ctor_t    = void (*) (Memory, void*);  // Converting constructor (via std::forward)
142 
143         typedef bool (*Equal)(const Memory, const Memory); // Equality test (external)
144 
cctrs()145         static constexpr std::array<CCtr, sizeof...(Ts)> cctrs(){ return {{(&cctr_h<Ts>::help)...}};}
mctrs()146         static constexpr std::array<MCtr, sizeof...(Ts)> mctrs(){ return {{(&mctr_h<Ts>::help)...}};}
cpyrs()147         static constexpr std::array<Copy, sizeof...(Ts)> cpyrs(){ return {{(&copy_h<Ts>::help)...}};}
mvers()148         static constexpr std::array<Move, sizeof...(Ts)> mvers(){ return {{(&move_h<Ts>::help)...}};}
swprs()149         static constexpr std::array<Swap, sizeof...(Ts)> swprs(){ return {{(&swap_h<Ts>::help)...}};}
dtors()150         static constexpr std::array<Dtor, sizeof...(Ts)> dtors(){ return {{(&dtor_h<Ts>::help)...}};}
151 
152         template<bool cond, typename T>
153         struct conditional_ref : std::conditional<cond, typename std::remove_reference<T>::type&, typename std::remove_reference<T>::type > {};
154 
155         template<bool cond, typename T>
156         using conditional_ref_t = typename conditional_ref<cond, T>::type;
157 
158 
159         template<bool is_lvalue_arg>
cnvrt_assgnrs()160         static constexpr std::array<cnvrt_assgn_t, sizeof...(Ts)> cnvrt_assgnrs(){
161             return {{(&cnvrt_assign_h<conditional_ref_t<is_lvalue_arg,Ts>>::help)...}};
162         }
163 
164         template<bool is_lvalue_arg>
cnvrt_ctors()165         static constexpr std::array<cnvrt_ctor_t, sizeof...(Ts)> cnvrt_ctors(){
166             return {{(&cnvrt_ctor_h<conditional_ref_t<is_lvalue_arg,Ts>>::help)...}};
167         }
168 
169         std::size_t m_index = 0;
170 
171     protected:
172         template<typename T, typename... Us> friend T& get(variant<Us...> &v);
173         template<typename T, typename... Us> friend const T& get(const variant<Us...> &v);
174         template<typename T, typename... Us> friend T* get_if(variant<Us...> *v) noexcept;
175         template<typename T, typename... Us> friend const T* get_if(const variant<Us...> *v) noexcept;
176 
177         template<typename... Us> friend bool operator==(const variant<Us...> &lhs,
178                                                         const variant<Us...> &rhs);
179         Memory memory;
180 
181     public:
182         // Constructors
183         variant() noexcept;
184         variant(const variant& other);
185         variant(variant&& other) noexcept;
186         // are_different_t is a SFINAE trick to avoid variant(T &&t) with T=variant
187         // for some reason, this version is called instead of variant(variant&& o) when
188         // variant is used in STL containers (examples: vector assignment).
189         template<
190             typename T,
191             typename = util::are_different_t<variant, T>
192         >
193         explicit variant(T&& t);
194         // template<class T, class... Args> explicit variant(Args&&... args);
195         // FIXME: other constructors
196 
197         // Destructor
198         ~variant();
199 
200         // Assignment
201         variant& operator=(const variant& rhs);
202         variant& operator=(variant &&rhs) noexcept;
203 
204         // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above
205         template<
206             typename T,
207             typename = util::are_different_t<variant, T>
208         >
209         variant& operator=(T&& t) noexcept;
210 
211         // Observers
212         std::size_t index() const noexcept;
213         // FIXME: valueless_by_exception()
214 
215         // Modifiers
216         // FIXME: emplace()
217         void swap(variant &rhs) noexcept;
218 
219         // Non-C++17x!
220         template<typename T> static constexpr std::size_t index_of();
221     };
222 
223     // FIMXE: visit
224     template<typename T, typename... Types>
225     T* get_if(util::variant<Types...>* v) noexcept;
226 
227     template<typename T, typename... Types>
228     const T* get_if(const util::variant<Types...>* v) noexcept;
229 
230     template<typename T, typename... Types>
231     T& get(util::variant<Types...> &v);
232 
233     template<typename T, typename... Types>
234     const T& get(const util::variant<Types...> &v);
235 
236     template<typename T, typename... Types>
237     bool holds_alternative(const util::variant<Types...> &v) noexcept;
238 
239     // FIXME: T&&, const TT&& versions.
240 
241     // Implementation //////////////////////////////////////////////////////////
242     template<typename... Ts>
variant()243     variant<Ts...>::variant() noexcept
244     {
245         typedef typename std::tuple_element<0, std::tuple<Ts...> >::type TFirst;
246         new (memory) TFirst();
247     }
248 
249     template<typename... Ts>
variant(const variant & other)250     variant<Ts...>::variant(const variant &other)
251         : m_index(other.m_index)
252     {
253         (cctrs()[m_index])(memory, other.memory);
254     }
255 
256     template<typename... Ts>
variant(variant && other)257     variant<Ts...>::variant(variant &&other) noexcept
258         : m_index(other.m_index)
259     {
260         (mctrs()[m_index])(memory, other.memory);
261     }
262 
263     template<typename... Ts>
264     template<class T, typename>
variant(T && t)265     variant<Ts...>::variant(T&& t)
266         : m_index(util::type_list_index<util::decay_t<T>, Ts...>::value)
267     {
268         const constexpr bool is_lvalue_arg =  std::is_lvalue_reference<T>::value;
269         (cnvrt_ctors<is_lvalue_arg>()[m_index])(memory, const_cast<util::decay_t<T> *>(&t));
270     }
271 
272     template<typename... Ts>
~variant()273     variant<Ts...>::~variant()
274     {
275         (dtors()[m_index])(memory);
276     }
277 
278     template<typename... Ts>
operator =(const variant<Ts...> & rhs)279     variant<Ts...>& variant<Ts...>::operator=(const variant<Ts...> &rhs)
280     {
281         if (m_index != rhs.m_index)
282         {
283             (dtors()[    m_index])(memory);
284             (cctrs()[rhs.m_index])(memory, rhs.memory);
285             m_index = rhs.m_index;
286         }
287         else
288         {
289             (cpyrs()[rhs.m_index])(memory, rhs.memory);
290         }
291         return *this;
292     }
293 
294     template<typename... Ts>
operator =(variant<Ts...> && rhs)295     variant<Ts...>& variant<Ts...>::operator=(variant<Ts...> &&rhs) noexcept
296     {
297         if (m_index != rhs.m_index)
298         {
299             (dtors()[    m_index])(memory);
300             (mctrs()[rhs.m_index])(memory, rhs.memory);
301             m_index = rhs.m_index;
302         }
303         else
304         {
305             (mvers()[rhs.m_index])(memory, rhs.memory);
306         }
307         return *this;
308     }
309 
310     template<typename... Ts>
311     template<typename T, typename>
operator =(T && t)312     variant<Ts...>& variant<Ts...>::operator=(T&& t) noexcept
313     {
314         using decayed_t = util::decay_t<T>;
315         // FIXME: No version with implicit type conversion available!
316         const constexpr std::size_t t_index =
317             util::type_list_index<decayed_t, Ts...>::value;
318 
319         const constexpr bool is_lvalue_arg =  std::is_lvalue_reference<T>::value;
320 
321         if (t_index != m_index)
322         {
323             (dtors()[m_index])(memory);
324             (cnvrt_ctors<is_lvalue_arg>()[t_index])(memory, &t);
325             m_index = t_index;
326         }
327         else
328         {
329             (cnvrt_assgnrs<is_lvalue_arg>()[m_index])(memory, &t);
330         }
331         return *this;
332 
333     }
334 
335     template<typename... Ts>
index() const336     std::size_t util::variant<Ts...>::index() const noexcept
337     {
338         return m_index;
339     }
340 
341     template<typename... Ts>
swap(variant<Ts...> & rhs)342     void variant<Ts...>::swap(variant<Ts...> &rhs) noexcept
343     {
344         if (m_index == rhs.index())
345         {
346             (swprs()[m_index](memory, rhs.memory));
347         }
348         else
349         {
350             variant<Ts...> tmp(std::move(*this));
351             *this = std::move(rhs);
352             rhs   = std::move(tmp);
353         }
354     }
355 
356     template<typename... Ts>
357     template<typename T>
index_of()358     constexpr std::size_t variant<Ts...>::index_of()
359     {
360         return util::type_list_index<T, Ts...>::value; // FIXME: tests!
361     }
362 
363     template<typename T, typename... Types>
364     T* get_if(util::variant<Types...>* v) noexcept
365     {
366         const constexpr std::size_t t_index =
367             util::type_list_index<T, Types...>::value;
368 
369         if (v && v->index() == t_index)
370             return (T*)(&v->memory);  // workaround for ICC 2019
371             // original code: return reinterpret_cast<T&>(v.memory);
372         return nullptr;
373     }
374 
375     template<typename T, typename... Types>
get_if(const util::variant<Types...> * v)376     const T* get_if(const util::variant<Types...>* v) noexcept
377     {
378         const constexpr std::size_t t_index =
379             util::type_list_index<T, Types...>::value;
380 
381         if (v && v->index() == t_index)
382             return (const T*)(&v->memory);  // workaround for ICC 2019
383             // original code: return reinterpret_cast<const T&>(v.memory);
384         return nullptr;
385     }
386 
387     template<typename T, typename... Types>
388     T& get(util::variant<Types...> &v)
389     {
390         if (auto* p = get_if<T>(&v))
391             return *p;
392         else
393             throw_error(bad_variant_access());
394     }
395 
396     template<typename T, typename... Types>
get(const util::variant<Types...> & v)397     const T& get(const util::variant<Types...> &v)
398     {
399         if (auto* p = get_if<T>(&v))
400             return *p;
401         else
402             throw_error(bad_variant_access());
403     }
404 
405     template<typename T, typename... Types>
holds_alternative(const util::variant<Types...> & v)406     bool holds_alternative(const util::variant<Types...> &v) noexcept
407     {
408         return v.index() == util::variant<Types...>::template index_of<T>();
409     }
410 
operator ==(const variant<Us...> & lhs,const variant<Us...> & rhs)411     template<typename... Us> bool operator==(const variant<Us...> &lhs,
412                                              const variant<Us...> &rhs)
413     {
414         using V = variant<Us...>;
415 
416         // Instantiate table only here since it requires operator== for <Us...>
417         // <Us...> should have operator== only if this one is used, not in general
418         static const std::array<typename V::Equal, sizeof...(Us)> eqs = {
419             {(&V::template equal_h<Us>::help)...}
420         };
421         if (lhs.index() != rhs.index())
422             return false;
423         return (eqs[lhs.index()])(lhs.memory, rhs.memory);
424     }
425 
operator !=(const variant<Us...> & lhs,const variant<Us...> & rhs)426     template<typename... Us> bool operator!=(const variant<Us...> &lhs,
427                                              const variant<Us...> &rhs)
428     {
429         return !(lhs == rhs);
430     }
431 } // namespace cv
432 } // namespace util
433 
434 #endif // OPENCV_GAPI_UTIL_VARIANT_HPP
435