/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef EINA_VARIANT_HH_ #define EINA_VARIANT_HH_ #include #include #include #include #include #include #include namespace efl { namespace eina { namespace _impl { template struct is_one_of : std::conditional::value , std::is_same , is_one_of >::type::type {}; template struct is_one_of : std::is_same {}; template struct find_impl : find_impl {}; template struct find_impl : std::integral_constant {}; template struct find : find_impl<0u, T, U, Args...> {}; } template struct call_visitor { template static typename F::result_type call(int type, void const* buffer, F f) { if(type == N) { using std::tuple_element; typedef typename tuple_element::type type; type const* o = static_cast(buffer); return f(*o); } else return call_visitor::call(type, buffer, f); } template static typename F::result_type call(int type, void* buffer, F f) { if(type == N) { using std::tuple_element; typedef typename tuple_element::type type; type* o = static_cast(buffer); return f(*o); } else return call_visitor::call(type, buffer, f); } }; template struct call_visitor { template static typename F::result_type call(int, void const*, F) { std::abort(); } }; struct compare_equal_visitor { void const* buffer; typedef bool result_type; template bool operator()(T const& other) const { return *static_cast(buffer) == other; } }; struct copy_visitor { typedef void result_type; void* buffer; template void operator()(T const& other) const { new (buffer) T(other); } }; struct move_visitor { typedef void result_type; void* buffer; template void operator()(T& other) const { typedef typename std::remove_cv::type>::type type; new (buffer) type(std::move(other)); } }; struct assign_visitor { typedef void result_type; void* buffer; template void operator()(T const& other) const { typedef typename std::remove_cv::type>::type type; type* assigned = static_cast(buffer); *assigned = other; } }; struct move_assign_visitor { typedef void result_type; void* buffer; template void operator()(T& other) const { typedef typename std::remove_cv::type>::type type; type* assigned = static_cast(buffer); *assigned = std::move(other); } }; struct destroy_visitor { typedef void result_type; template void operator()(T&& other) const noexcept { typedef typename std::remove_cv::type>::type type; other.~type(); } }; struct ostream_visitor { std::ostream* s; typedef std::ostream& result_type; template std::ostream& operator()(T const& other) const { return *s << other; } }; template struct get_visitor { typedef T* result_type; T* operator()(T& object) const { return &object; } template T* operator()(U&) const { return nullptr; } }; template struct variant { typedef variant _self_type; /**< Type for the optional class itself. */ constexpr variant() : type(-1) {} template variant(T object, typename std::enable_if<_impl::is_one_of ::type>::type, Args...>::value>::type* = 0) : type(_impl::find::type>::type, Args...>::value) { construct(object); } variant(variant const& other) : type(other.type) { if(other.type != -1) other.visit(copy_visitor{static_cast(&buffer)}); } variant& operator=(variant const& other) { if(type == other.type && type != -1) { other.visit(assign_visitor{static_cast(&buffer)}); } else if(type != other.type) { if(type != -1) destroy_unsafe(); type = other.type; other.visit(copy_visitor{static_cast(&buffer)}); } return *this; } variant(variant&& other) : type(other.type) { if(other.type != -1) other.visit(move_visitor{static_cast(&buffer)}); } variant& operator=(variant&& other) { if(type == other.type && type != -1) { other.visit(move_assign_visitor{static_cast(&buffer)}); } else if(type != other.type) { if(type != -1) destroy_unsafe(); type = other.type; other.visit(move_visitor{static_cast(&buffer)}); } return *this; } ~variant() { if(type != -1) destroy_unsafe(); } void destroy() { if(type != -1) { destroy_unsafe(); type = -1; } } void destroy_unsafe() { visit_unsafe(destroy_visitor()); } bool empty() const { return type == -1; } template typename F::result_type visit(F f) const { if(type == -1) { throw std::runtime_error("variant is empty"); } else return call_visitor<0u, sizeof...(Args), std::tuple>::call(type, static_cast(&buffer), f); } template typename F::result_type visit(F f) { if(type == -1) { throw std::runtime_error("variant is empty"); } else return call_visitor<0u, sizeof...(Args), std::tuple>::call(type, static_cast(&buffer), f); } template typename F::result_type visit_unsafe(F f) const { return call_visitor<0u, sizeof...(Args), std::tuple>::call(type, static_cast(&buffer), f); } template typename F::result_type visit_unsafe(F f) { return call_visitor<0u, sizeof...(Args), std::tuple>::call(type, static_cast(&buffer), f); } private: template void construct(T object) { new (&buffer) T(std::move(object)); } typedef typename eina::aligned_union<1, Args...>::type buffer_type; friend bool operator==(variant const& lhs, variant const& rhs) { return rhs.type == lhs.type && (rhs.type == -1 || rhs.visit(compare_equal_visitor{&lhs.buffer})); } friend std::ostream& operator<<(std::ostream& s, variant const& rhs) { return rhs.visit(ostream_visitor{&s}); } int type; /** * Member variable for holding the contained value. */ buffer_type buffer; }; template inline bool operator!=(variantconst& lhs, variant const& rhs) { return !(lhs == rhs); } template T* get(variant* variant, typename std::enable_if<_impl::is_one_of ::type>::type, Args...>::value>::type* = 0) { return variant->visit(get_visitor{}); } template T const* get(variantconst* variant, typename std::enable_if<_impl::is_one_of ::type>::type, Args...>::value>::type* = 0) { return variant->visit(get_visitor{}); } template T& get(variant& variant, typename std::enable_if<_impl::is_one_of ::type>::type, Args...>::value>::type* = 0) { T* p = variant.visit(get_visitor{}); if(p) return *p; else throw std::logic_error(""); } template T const& get(variantconst& variant, typename std::enable_if<_impl::is_one_of ::type>::type, Args...>::value>::type* = 0) { T const* p = variant.visit(get_visitor{}); if(p) return *p; else throw std::logic_error(""); } } } #endif