// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // // Copyright (C) 2018 Intel Corporation #ifndef OPENCV_GAPI_UTIL_VARIANT_HPP #define OPENCV_GAPI_UTIL_VARIANT_HPP #include #include #include "opencv2/gapi/util/throw.hpp" #include "opencv2/gapi/util/util.hpp" // max_of_t // A poor man's `variant` implementation, incompletely modeled against C++17 spec. namespace cv { namespace util { namespace detail { template struct type_list_index_helper { static const constexpr bool is_same = std::is_same::value; static const constexpr std::size_t value = std::conditional, type_list_index_helper>::type::value; }; template struct type_list_index_helper { static_assert(std::is_same::value, "Type not found"); static const constexpr std::size_t value = I; }; template using are_different = std::enable_if::type, typename std::decay::type>::value, V>; } template struct type_list_index { static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value; }; class bad_variant_access: public std::exception { public: virtual const char *what() const noexcept override { return "Bad variant access"; } }; // Interface /////////////////////////////////////////////////////////////// struct monostate {}; inline bool operator==(const util::monostate&, const util::monostate&) { return true; } template // FIXME: no references, arrays, and void class variant { // FIXME: Replace with std::aligned_union after gcc4.8 support is dropped static constexpr const std::size_t S = cv::detail::max_of_t::value; static constexpr const std::size_t A = cv::detail::max_of_t::value; using Memory = typename std::aligned_storage::type[1]; template struct cctr_h { static void help(Memory memory, const Memory from) { new (memory) T(*reinterpret_cast(from)); } }; template struct vctr_h { static void help(Memory memory, const void* pval) { new (memory) T(*reinterpret_cast(pval)); } }; template struct mctr_h { static void help(Memory memory, void *pval) { new (memory) T(std::move(*reinterpret_cast(pval))); } }; template struct copy_h { static void help(Memory to, const Memory from) { *reinterpret_cast(to) = *reinterpret_cast(from); } }; template struct move_h { static void help(Memory to, const Memory from) { *reinterpret_cast(to) = std::move(*reinterpret_cast(from)); } }; template struct swap_h { static void help(Memory to, Memory from) { std::swap(*reinterpret_cast(to), *reinterpret_cast(from)); } }; template struct dtor_h { static void help(Memory memory) { (void) memory; // MSCV warning reinterpret_cast(memory)->~T(); } }; template struct equal_h { static bool help(const Memory lhs, const Memory rhs) { const T& t_lhs = *reinterpret_cast(lhs); const T& t_rhs = *reinterpret_cast(rhs); return t_lhs == t_rhs; } }; typedef void (*CCtr) (Memory, const Memory); // Copy c-tor (variant) typedef void (*VCtr) (Memory, const void*); // Copy c-tor (value) typedef void (*MCtr) (Memory, void*); // Generic move c-tor typedef void (*Copy) (Memory, const Memory); // Copy assignment typedef void (*Move) (Memory, const Memory); // Move assignment typedef void (*Swap) (Memory, Memory); // Swap typedef void (*Dtor) (Memory); // Destructor typedef bool (*Equal)(const Memory, const Memory); // Equality test (external) static constexpr std::array cctrs(){ return {{(&cctr_h::help)...}};} static constexpr std::array vctrs(){ return {{(&vctr_h::help)...}};} static constexpr std::array mctrs(){ return {{(&mctr_h::help)...}};} static constexpr std::array cpyrs(){ return {{(©_h::help)...}};} static constexpr std::array mvers(){ return {{(&move_h::help)...}};} static constexpr std::array swprs(){ return {{(&swap_h::help)...}};} static constexpr std::array dtors(){ return {{(&dtor_h::help)...}};} std::size_t m_index = 0; protected: template friend T& get(variant &v); template friend const T& get(const variant &v); template friend bool operator==(const variant &lhs, const variant &rhs); Memory memory; public: // Constructors variant() noexcept; variant(const variant& other); variant(variant&& other) noexcept; template explicit variant(const T& t); // are_different is a SFINAE trick to avoid variant(T &&t) with T=variant // for some reason, this version is called instead of variant(variant&& o) when // variant is used in STL containers (examples: vector assignment) template explicit variant(T&& t, typename detail::are_different::type = 0); // template explicit variant(Args&&... args); // FIXME: other constructors // Destructor ~variant(); // Assignment variant& operator=(const variant& rhs); variant& operator=(variant &&rhs) noexcept; // SFINAE trick to avoid operator=(T&&) with T=variant<>, see comment above template typename detail::are_different ::type operator=(T&& t) noexcept; // Observers std::size_t index() const noexcept; // FIXME: valueless_by_exception() // Modifiers // FIXME: emplace() void swap(variant &rhs) noexcept; // Non-C++17x! template static constexpr std::size_t index_of(); }; // FIMXE: visit template T& get(util::variant &v); template const T& get(const util::variant &v); template bool holds_alternative(const util::variant &v) noexcept; // FIXME: T&&, const TT&& versions. // Implementation ////////////////////////////////////////////////////////// template variant::variant() noexcept { typedef typename std::tuple_element<0, std::tuple >::type TFirst; new (memory) TFirst(); } template variant::variant(const variant &other) : m_index(other.m_index) { (cctrs()[m_index])(memory, other.memory); } template variant::variant(variant &&other) noexcept : m_index(other.m_index) { (mctrs()[m_index])(memory, other.memory); } template template variant::variant(const T& t) : m_index(util::type_list_index::value) { (vctrs()[m_index])(memory, &t); } template template variant::variant(T&& t, typename detail::are_different::type) : m_index(util::type_list_index::type, Ts...>::value) { (mctrs()[m_index])(memory, &t); } template variant::~variant() { (dtors()[m_index])(memory); } template variant& variant::operator=(const variant &rhs) { if (m_index != rhs.m_index) { (dtors()[ m_index])(memory); (cctrs()[rhs.m_index])(memory, rhs.memory); m_index = rhs.m_index; } else { (cpyrs()[rhs.m_index])(memory, rhs.memory); } return *this; } template variant& variant::operator=(variant &&rhs) noexcept { if (m_index != rhs.m_index) { (dtors()[ m_index])(memory); (mctrs()[rhs.m_index])(memory, rhs.memory); m_index = rhs.m_index; } else { (mvers()[rhs.m_index])(memory, rhs.memory); } return *this; } template template typename detail::are_different, T, variant&> ::type variant::operator=(T&& t) noexcept { // FIXME: No version with implicit type conversion available! static const constexpr std::size_t t_index = util::type_list_index::value; if (t_index == m_index) { util::get(*this) = std::move(t); return *this; } else return (*this = variant(std::move(t))); } template std::size_t util::variant::index() const noexcept { return m_index; } template void variant::swap(variant &rhs) noexcept { if (m_index == rhs.index()) { (swprs()[m_index](memory, rhs.memory)); } else { variant tmp(std::move(*this)); *this = std::move(rhs); rhs = std::move(tmp); } } template template constexpr std::size_t variant::index_of() { return util::type_list_index::value; // FIXME: tests! } template T& get(util::variant &v) { const constexpr std::size_t t_index = util::type_list_index::value; if (v.index() == t_index) return *(T*)(&v.memory); // workaround for ICC 2019 // original code: return reinterpret_cast(v.memory); else throw_error(bad_variant_access()); } template const T& get(const util::variant &v) { const constexpr std::size_t t_index = util::type_list_index::value; if (v.index() == t_index) return *(const T*)(&v.memory); // workaround for ICC 2019 // original code: return reinterpret_cast(v.memory); else throw_error(bad_variant_access()); } template bool holds_alternative(const util::variant &v) noexcept { return v.index() == util::variant::template index_of(); } template bool operator==(const variant &lhs, const variant &rhs) { using V = variant; // Instantiate table only here since it requires operator== for // should have operator== only if this one is used, not in general static const std::array eqs = { {(&V::template equal_h::help)...} }; if (lhs.index() != rhs.index()) return false; return (eqs[lhs.index()])(lhs.memory, rhs.memory); } template bool operator!=(const variant &lhs, const variant &rhs) { return !(lhs == rhs); } } // namespace cv } // namespace util #endif // OPENCV_GAPI_UTIL_VARIANT_HPP