/* * 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 #include namespace efl { namespace eina { template struct variant; template struct variant_size : std::tuple_size>::type { }; template struct variant_as_tuple; 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 visit_impl_meta_args { typedef std::integral_constant current_type_index; typedef std::integral_constant current_variant_index; typedef TupleVariant variants; // typedef typename std::tuple_element::type current_variant; // typedef typename variant_as_tuple::type current_variant_types; // typedef typename std::tuple_element::type current_type; typedef TypesFound types_found; }; template struct current_variant_types { typedef typename std::tuple_element::type current_variant; typedef typename variant_as_tuple::type type; }; } // template // struct call_n_visitor; // template // struct call_n_visitor // { /* 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_n_visitor // { // template // static typename F::result_type call(int, void const*, F, Variants&&... variants) // { // std::abort(); // } // }; 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); } constexpr std::size_t index() const { return type; } 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 friend struct variant_as_tuple; // template // friend typename F::result_type visit_impl2 // (std::false_type, std::false_type // , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants, index_sequence); // template // friend typename F::result_type visit_impl2 // (std::true_type, std::false_type // , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants, index_sequence); // template // friend typename F::result_type visit_impl2 // (std::false_type, std::true_type // , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants, index_sequence); // // template // // friend typename F::result_type visit_impl2 // // (std::true_type, std::false_type // // , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants) // // { // // } // // template // // friend typename F::result_type call // // (std::true_type, int, F &&, Variants&&... variants) // // { // // std::abort(); // // } // // template // // friend typename F::result_type call (F&&f, Variant&& variant, Variants&&... variants) // // { // // return call (std::integral_constant::value)>{} // // , variant.type, std::forward(f), std::forward(variant), std::forward(variants)...); // // } // // template // // friend typename F::result_type call (F&&f, Variant&& variant, Variants&&... variants) // // { // // return call (std::integral_constant::value)>{} // // , variant.type, std::forward(f), std::forward(variant), std::forward(variants)...); // // } // template // friend typename F::result_type visit_impl (F&& f, Variants&&... variants); }; template struct variant_as_tuple> { typedef std::tuple type; }; template struct variant_as_tuple> { typedef std::tuple type; }; template struct variant_as_tuple> { typedef std::tuple type; }; template struct variant_as_tuple> { typedef std::tuple type; }; template struct variant_as_tuple&> { typedef std::tuple type; }; template struct variant_as_tuple&> { typedef std::tuple type; }; template struct variant_as_tuple&> { typedef std::tuple type; }; template struct variant_as_tuple&> { typedef std::tuple type; }; 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(""); } template typename F::result_type visit_impl2 (std::false_type, std::true_type , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants , eina::index_sequence) { return f (eina::get::type>(std::get(variants))...); } template typename F::result_type visit_impl2 (std::true_type, std::false_type , _impl::visit_impl_meta_args, F&&, TupleVariant&&, eina::index_sequence) { std::abort(); } template typename F::result_type visit_impl2 (std::false_type, std::false_type , _impl::visit_impl_meta_args, F&& f, TupleVariant&& variants , index_sequence) { using std::tuple_element; typedef _impl::visit_impl_meta_args meta_args; if(std::get(variants).index() == NT) { typedef typename _impl::current_variant_types::type variant_types; typedef typename tuple_element::type type; std::integral_constant::value == NV+1)> is_true {}; return visit_impl2( std::false_type{} , is_true , _impl::visit_impl_meta_args<0u, NV+1, Tuple, typename _mpl::push_back::type>{} , std::forward(f), std::forward(variants) , make_index_sequence::value>{}); } else { typedef typename _impl::current_variant_types::type variant_types; return visit_impl2 (std::integral_constant::value == NT+1)>{} , std::false_type{} , _impl::visit_impl_meta_args{}, std::forward(f), std::forward(variants) , make_index_sequence::value>{}); } } template typename F::result_type visit_impl_aux (std::false_type fals, std::false_type , _impl::visit_impl_meta_args args, F&& f, TupleVariant&& variants) { return visit_impl2 (fals, fals, args, std::forward(f), std::forward(variants) , make_index_sequence::value>{}); } template typename F::result_type visit_impl (F&& f, Variants&&... variants) { return visit_impl_aux (std::false_type{} , std::false_type{} , _impl::visit_impl_meta_args <0u, 0u , std::tuple::type...>, std::tuple<>>{}, std::forward(f), std::forward_as_tuple(std::forward(variants)...)); } template auto visit (F&& function, Variants&& ... variants) -> typename F::result_type { return visit_impl (std::forward(function), std::forward(variants)...); } } } #endif