// elm architecture #include #include #include namespace fplus { namespace internal { // http://stackoverflow.com/a/18987405/1866775 template struct is_one_of; template struct is_one_of { static constexpr bool value = false; }; template struct is_one_of { static constexpr bool value = std::is_same::value || is_one_of::value; }; template struct is_one_of> { static constexpr bool value = is_one_of::value; }; template struct is_unique; template <> struct is_unique<> { static constexpr bool value = true; }; template struct is_unique { static constexpr bool value = is_unique::value && !is_one_of::value; }; template struct is_unique> { static constexpr bool value = is_unique::value; }; template struct are_same; template <> struct are_same<> { static constexpr bool value = true; }; template struct are_same { static constexpr bool value = std::is_same::value; }; template struct are_same { static constexpr bool value = are_same::value && std::is_same::value; }; template struct are_same> { static constexpr bool value = are_same::value; }; // http://stackoverflow.com/a/3273571/1866775 template class List, template class Mod, typename ...Args> struct transform_parameter_pack { typedef List::type...> type; }; template struct as_shared_pointer { typedef std::shared_ptr type; }; // http://stackoverflow.com/a/27588263/1866775 template struct get_index; template struct get_index : std::integral_constant {}; template struct get_index : std::integral_constant::value> {}; template struct get_index { // condition is always false, but should be dependant of T static_assert(sizeof(T) == 0, "element not found"); }; template struct parameter_pack_head { typedef T type; }; template struct function_first_input_type { typedef typename std::remove_const< typename std::remove_reference< typename utils::function_traits< F>::template arg<0>::type>::type>::type type; }; template struct unary_function_result_type { static_assert(utils::function_traits::arity == 1, "Wrong arity."); typedef typename function_first_input_type::type T; typedef std::decay_t> type; }; // http://stackoverflow.com/a/42493805/1866775 template struct tag { }; template struct type_set_eq_helper: tag... { }; template struct type_set_eq: std::false_type { }; template struct bool_pack { }; template using my_and = std::is_same, bool_pack>; template struct type_set_eq, std::tuple, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of, type_set_eq_helper>::value... >::value >::type >: std::true_type { }; // http://stackoverflow.com/a/42581257/1866775 template struct is_superset_of; template struct is_superset_of> { static const bool value = is_one_of::value && is_superset_of>::value; }; template struct is_superset_of> { static const bool value = true; }; // http://stackoverflow.com/a/36934374/1866775 template using all_true = std::is_same, bool_pack>; } // namespace internal template struct variant { static_assert(internal::is_unique::value, "Types must be unique."); static_assert(internal::all_true<(!std::is_reference::value)...>::value, "No reference types allowed."); static_assert(internal::all_true<(!std::is_const::value)...>::value, "No const types allowed."); static_assert(sizeof...(Types) >= 1, "Please provide at least one type."); template variant(const T& val) : shared_ptrs_({}) { std::get::value>(shared_ptrs_) = std::make_shared(val); } template bool is() const { static_assert( internal::is_one_of::value , "Type must match one possible variant type."); const auto ptr = std::get::value>(shared_ptrs_); return static_cast(ptr); } friend bool operator== ( const variant& a, const variant& b) { return a.shared_ptrs_ == b.shared_ptrs_; } friend bool operator!= ( const variant& a, const variant& b) { return a.shared_ptrs_ != b.shared_ptrs_; } template auto visit_one(F f) const { using T = typename internal::function_first_input_type::type; using Ret = internal::invoke_result_t; internal::trigger_static_asserts(); static_assert( internal::is_one_of< typename internal::function_first_input_type::type, Types...>::value , "Function input must match one variant type."); static_assert(!std::is_same, void>::value, "Function must return non-void type."); const auto ptr = std::get::value>(shared_ptrs_); if (ptr) { return just(internal::invoke(f, *ptr)); } return nothing>(); } template auto visit(Fs ... fs) const -> typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type { typedef typename internal::unary_function_result_type< typename internal::parameter_pack_head::type>::type Res; static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::is_unique::value, "Only one function per input type allowed."); static_assert( internal::are_same::value, "All Functions must return the same type."); static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); const auto results = justs(visit_helper(fs...)); assert(size_of_cont(results) == 1); return head(results); } template variant transform(Fs ... fs) const { static_assert( sizeof...(Fs) >= std::tuple_size::value, "Too few functions provided."); static_assert( sizeof...(Fs) <= std::tuple_size::value, "Too many functions provided."); typedef typename internal::transform_parameter_pack< std::tuple, internal::unary_function_result_type, Fs... >::type return_types_tuple; typedef typename internal::transform_parameter_pack< std::tuple, internal::function_first_input_type, Fs... >::type function_first_input_types_tuple; static_assert( internal::type_set_eq>::value, "Functions do not cover all possible types."); static_assert( internal::is_superset_of, return_types_tuple>::value, "All Functions must return a possible variant type."); return visit(fs...); } private: template std::vector> visit_helper(F f) const { return {visit_one(f)}; } template std::vector> visit_helper(F f, Fs ... fs) const { return fplus::append(visit_helper(f), visit_helper(fs...)); } typedef typename internal::transform_parameter_pack< std::tuple, internal::as_shared_pointer, Types... >::type shared_ptr_pack; shared_ptr_pack shared_ptrs_; }; } // namespace fplus