/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net> */ #pragma once #include "../types.hpp" #include "../types/traits.hpp" #include <cstddef> #include <tuple> #include <functional> namespace cruft::tuple::type { /////////////////////////////////////////////////////////////////////////// /// Invoke a provided FunctorT with the provided arguments and a /// type_tag<T> for each type in the tuple-like type TupleT. /// /// The order of invocation is low-index to high-index (left to right). /// /// \tparam TupleT A tuple-like type /// \tparam FunctorT An invokable type /// \tparam S The index to be invoked; this is private. /// \tparam ArgsT User supplied arguments template< typename TupleT, typename FunctorT, size_t S = 0, typename ...ArgsT > void each (FunctorT &&func, ArgsT &&...args) { /// Get a handle on the type we need to forward. static_assert (S < std::tuple_size_v<TupleT>); using value_type = typename std::tuple_element<S,TupleT>::type; /// Call the user's invokable. std::invoke (func, args..., cruft::type_tag<value_type> {}); /// Recurse into ourselves with a higher index if we haven't reached /// the end. if constexpr (S + 1 < std::tuple_size_v<TupleT>) { each<TupleT,FunctorT,S+1> ( std::forward<FunctorT> (func), std::forward<ArgsT> (args)... ); } } /////////////////////////////////////////////////////////////////////////////// /// Convert a tuple-type holding an initial set of types into a tuple-type /// holding a different set of types using the supplied transform: /// `FieldT<...>`. /// /// \tparam TupleT The initial tuple-type /// \tparam FieldT The type-transform type. /// The conversion uses FieldT<...>::type to perform the /// conversions. template < typename TupleT, template < typename > class FieldT, typename Indices = std::make_index_sequence< std::tuple_size<TupleT>::value > > struct map; //----------------------------------------------------------------------------- template < typename TupleT, template < typename > class FieldT, size_t ...Indices > struct map< TupleT, FieldT, std::index_sequence<Indices...> > { typedef std::tuple< typename FieldT< typename std::tuple_element<Indices, TupleT>::type >::type... > type; }; /////////////////////////////////////////////////////////////////////////// /// Query the index of the first occurrence of type `T' in the tuple-like /// type `TupleT'. /// /// If the query type does not occur in the tuple type a compiler error /// should be generated. /// /// \tparam TupleT The tuple-type to be queried /// \tparam ValueT The value-type we are searching for /// /// The index will be defined as the size_t `::value` if it has been found. template< typename TupleT, typename ValueT, typename = std::make_index_sequence< std::tuple_size_v<TupleT> > > struct index { }; //------------------------------------------------------------------------- // specialisation for tuples with matching first elements. the index is 0. template< typename ValueT, typename ...TailT, size_t ...Indices > struct index< std::tuple<ValueT, TailT...>, ValueT, std::index_sequence<Indices...> > { static constexpr std::size_t value = 0; }; //------------------------------------------------------------------------- // specialisation for tuples with non-matching first elements. // increment and recurse. template< typename ValueT, typename HeadT, typename ...TailT, size_t ...Indices > struct index< std::tuple<HeadT, TailT...>, ValueT, std::index_sequence<Indices...> > { static constexpr std::size_t value = 1u + index<std::tuple<TailT...>,ValueT>::value; }; //------------------------------------------------------------------------- // convert the tuple type (which is not a tuple or it would have matched // the tuple specialisation, into the equivalent std::tuple and requery // against that. template < typename TupleT, typename ValueT, size_t ...Indices > struct index< TupleT, ValueT, std::index_sequence<Indices...> > { static constexpr std::size_t value = index< std::tuple<std::tuple_element_t<Indices,TupleT>...>, ValueT, std::index_sequence<Indices...> >::value; }; /////////////////////////////////////////////////////////////////////////// /// Transform a tuple-like type (ie, something that responds to /// tuple_element) into a tuple template < typename TupleT, typename Indices = std::make_index_sequence< std::tuple_size_v<TupleT> > > struct entuple; //------------------------------------------------------------------------- template < typename TupleT, size_t ...Indices > struct entuple< TupleT, std::index_sequence<Indices...> > { using type = std::tuple< std::tuple_element_t<Indices,TupleT>... >; }; //------------------------------------------------------------------------- template <typename T> using entuple_t = typename entuple<T>::type; /////////////////////////////////////////////////////////////////////////// /// Concatenate a variadic collection of tuple-like types template <typename ...Args> struct cat; //------------------------------------------------------------------------- template <> struct cat<> { using type = std::tuple<>; }; //------------------------------------------------------------------------- template <typename HeadT, typename ...TailT> struct cat<HeadT,TailT...> { using type = decltype( std::tuple_cat ( std::declval< entuple_t<HeadT> > (), std::declval< typename cat<TailT...>::type > () ) ); }; //------------------------------------------------------------------------- template <typename ...TupleT> using cat_t = typename cat<TupleT...>::type; /////////////////////////////////////////////////////////////////////////// /// Remove a type from a tuple-like type template < typename DoomedT, typename TupleT > struct remove; //------------------------------------------------------------------------- template < typename DoomedT > struct remove< DoomedT, std::tuple<> > { using type = std::tuple<>; }; //------------------------------------------------------------------------- template < typename DoomedT, typename HeadT, typename ...TailT > struct remove< DoomedT, std::tuple<HeadT,TailT...> > { using type = cat_t< std::conditional_t< std::is_same_v<DoomedT,HeadT>, std::tuple< >, std::tuple<HeadT> >, typename remove<DoomedT,std::tuple<TailT...>>::type >; }; //------------------------------------------------------------------------- template <typename DoomedT, typename TupleT> using remove_t = typename remove<DoomedT,TupleT>::type; /////////////////////////////////////////////////////////////////////////// /// Remove duplicate types from the list of held types in a tuple template <typename TupleT> struct unique; //------------------------------------------------------------------------- template <> struct unique<std::tuple<>> { using type = std::tuple<>; }; //------------------------------------------------------------------------- template <typename HeadT, typename ...TailT> struct unique< std::tuple<HeadT, TailT...> > { using type = cat_t< std::tuple<HeadT>, typename unique< typename remove< HeadT, std::tuple<TailT...> >::type >::type >; }; //------------------------------------------------------------------------- template <typename T> using unique_t = typename unique<T>::type; /////////////////////////////////////////////////////////////////////////// /// Convenience accessor for the 'nth' type of a tuple. template <typename TupleT, std::size_t IndexV> struct nth; //------------------------------------------------------------------------- template <typename HeadT, typename ...TailT> struct nth<std::tuple<HeadT,TailT...>,0> { using type = HeadT; }; //------------------------------------------------------------------------- template <typename HeadT, typename ...TailT, std::size_t IndexV> struct nth<std::tuple<HeadT,TailT...>,IndexV> : nth<std::tuple<TailT...>,IndexV-1> { }; //------------------------------------------------------------------------- template <typename TupleT, std::size_t IndexV> using nth_t = typename nth<TupleT, IndexV>::type; /////////////////////////////////////////////////////////////////////////// /// Extract the first CountV types of the tuple-like-object and store them /// in a tuple definition. template < std::size_t CountV, typename TupleT, typename Indices = std::make_index_sequence<CountV> > struct prefix; //------------------------------------------------------------------------- template < std::size_t CountV, typename TupleT, std::size_t ...IndexV > struct prefix< CountV, TupleT, std::index_sequence<IndexV...> > { using type = std::tuple< std::tuple_element_t<IndexV, TupleT>... >; }; //------------------------------------------------------------------------- template <std::size_t CountV, typename TupleT> using prefix_t = typename prefix<CountV, TupleT>::type; };