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 {{(©_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