Skip to content
Snippets Groups Projects
Forked from Core Modules / dune-common
4030 commits behind the upstream repository.
tupleutility.hh 20.29 KiB
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:

#ifndef DUNE_TUPLE_UTILITY_HH
#define DUNE_TUPLE_UTILITY_HH

#include <cstddef>
#include <tuple>

#include <dune/common/typetraits.hh>
#include <dune/common/std/type_traits.hh>

#include "tuples.hh"

namespace Dune {

  /** @ addtogroup Common
   *
   * @{
   */

  /**
   * @file
   * @brief Contains utility classes which can be used with std::tuple.
   */

  /**
   * @brief A helper template that initializes a std::tuple consisting of pointers
   * to nullptr.
   *
   * A std::tuple of nullptr may be useful when you use a std::tuple of pointers
   * in a class which you can only initialise in a later stage.
   */
  template<class T>
  struct NullPointerInitialiser;

  template<class... Args>
  struct NullPointerInitialiser<std::tuple<Args...> >
  {
    typedef std::tuple<Args...> ResultType;
    static ResultType apply()
    {
      return ResultType(static_cast<Args>(nullptr)...);
    }
  };

  /**
   * @brief Helper template to clone the type definition of a std::tuple with the
   * storage types replaced by a user-defined rule.
   *
   * Suppose all storage types A_i in a std::tuple define a type A_i::B. You can
   * build up a pair consisting of the types defined by A_i::B in the following
   * way:
   *
   * \code
   * template <class A>
   * struct MyEvaluator
   * {
   *   typedef typename A::B Type;
   * };
   *
   * typedef ForEachType<MyEvaluator, ATuple>::Type BTuple;
   * \endcode
   *
   * Here, MyEvaluator is a helper struct that extracts the correct type from
   * the storage types of the tuple defined by the tuple ATuple.
   *
   * \sa AddRefTypeEvaluator, AddPtrTypeEvaluator, genericTransformTuple(),
   *     and transformTuple().
   */
  template<template <class> class TE, class T>
  struct ForEachType;

  template<template <class> class TE, class... Args>
  struct ForEachType<TE, std::tuple<Args...> >
  {
    typedef std::tuple<typename TE<Args>::Type...> Type;
  };

#ifndef DOXYGEN
  template<class Tuple, class Functor, std::size_t... I>
  inline auto genericTransformTupleBackendImpl(Tuple& t, Functor& f, const Std::index_sequence<I...>& ) ->
    decltype(std::make_tuple(f(std::get<I>(t))...)) const
  {
    return std::make_tuple(f(std::get<I>(t))...);
  }

  template<class... Args, class Functor>
  auto genericTransformTupleBackend(std::tuple<Args...>& t, Functor& f) ->
    decltype(genericTransformTupleBackendImpl(t, f,Std::make_index_sequence<sizeof...(Args)>()))
  {
    return genericTransformTupleBackendImpl(t, f,Std::make_index_sequence<sizeof...(Args)>());
  }
#endif

  /**
   * This function does for the value of a std::tuple what ForEachType does for the
   * type of a std::tuple: it transforms the value using a user-provided policy
   * functor.
   *
   * \param t The std::tuple value to transform.
   * \param f The functor to use to transform the values.
   *
   * The functor should have the following form:
   *
   * \code
   * struct Functor
   * {
   *   template<class>
   *   struct TypeEvaluator
   *   {
   *     typedef user-defined Type;
   *   };
   *
   *   template<class T>
   *   typename TypeEvaluator<T>::Type operator()(T& val);
   *
   *   template<class T>
   *   typename TypeEvaluator<T>::Type operator()(T& val) const;
   *
   *   template<class T>
   *   typename TypeEvaluator<T>::Type operator()(const T& val);
   *
   *   template<class T>
   *   typename TypeEvaluator<T>::Type operator()(const T& val) const;
   * };
   * \endcode
   *
   * The member class template \c TypeEvaluator should be a class template
   * suitable as the \c TypeEvaluator template parameter for ForEachType.  The
   * function call operator \c operator() is used to transform the value; only
   * the signatures of \c operator() which are actually used must be present.
   */
  template<class Tuple, class Functor>
  auto genericTransformTuple(Tuple&& t, Functor&& f) ->
    decltype(genericTransformTupleBackend(t, f))
  {
    return genericTransformTupleBackend(t, f);
  }
  /**
   * \tparam TE TypeEvaluator class template.
   * \tparam An Type of extra arguments to pass to \c TE<T>::apply().  \c void
   *            means "no argument".  Only trailing arguments may be void.
   *
   * This class stores references to a number of arguments it receives in the
   * constructor.  Later, its function call operator \c operator() may be
   * called with a parameter \c t of type \c T.  \c operator() will then call
   * the static method \c TE<T>::apply(t,args...), where \c args... is the
   * sequence of arguments the object was constructed with.  \c operator()
   * will convert the result to type \c TE<T>::Type and return it.
   *
   * \c TE should be an extended version of the \c TypeEvaluator class
   * template parameter of ForEachType, for instance:
   *
   * \code
   * template <class T>
   * struct TypeEvaluator
   * {
   *   typedef T* Type;
   *   static Type apply(T& t, void* a0)
   *   {
   *     return t ? &t : static_cast<T*>(a0);
   *   }
   * };
   * \endcode
   *
   * In this example, for the value transformation, it takes a reference to a value
   * of type T and return the pointer to that value, unless the value evaluates to false
   * in boolean context.  If the value evaluates to false, it will instead return the
   * pointer from the extra argument.
   */
  template<template<class> class TE, class... Args>
  class TransformTupleFunctor
  {
    mutable std::tuple<Args&...> tup;

    template<class T, std::size_t... I>
    inline auto apply(T&& t, const Std::index_sequence<I...>& ) ->
      decltype(TE<T>::apply(t,std::get<I>(tup)...)) const
    {
      return TE<T>::apply(t,std::get<I>(tup)...);
    }

  public:
    template<class T>
    struct TypeEvaluator : public TE<T>
    {};

    TransformTupleFunctor(Args&&... args)
      : tup(args...)
    { }

    template<class T>
    inline auto operator()(T&& t) ->
      decltype(this->apply(t,Std::make_index_sequence<sizeof...(Args)>())) const
    {
      return apply(t,Std::make_index_sequence<sizeof...(Args)>());
    }
  };

  template<template<class> class TE, class... Args>
  TransformTupleFunctor<TE, Args...> makeTransformTupleFunctor(Args&&... args)
  {
    return TransformTupleFunctor<TE, Args...>(args...);
  }

  /**
   * This function provides functionality similar to genericTransformTuple(),
   * although less general and closer in spirit to ForEachType.
   *
   * \tparam TypeEvaluator Used as the \c TE template argument to
   *                       TransformTupleFunctor internally.
   * \tparam Tuple         Type of the std::tuple to transform.
   * \tparam Args          Types of extra argument to call the transformation
   *                       function with.
   *
   * \param orig Tuple value to be transformed.
   * \param args Extra arguments values to provide to the transformation
   *             function.
   *
   * The \c TypeEvaluator class template should be suitable as the \c TE
   * template argument for TransformTupleFunctor.  It has the following form
   * (an extension of the \c TypeEvaluator template argument of ForEachType):
   *
   * \code
   * template <class T>
   * struct TypeEvaluator
   * {
   *   typedef UserDefined Type;
   *
   *   template<class... Args>
   *   static Type apply(T& t, Args&... args);
   * };
   * \endcode
   *
   * \sa genericTransforTuple(), ForEachType, AddRefTypeEvaluator, and
   *     AddPtrTypeEvaluator.
   */
  template<template<class> class TypeEvaluator, class Tuple, class... Args>
  auto transformTuple(Tuple&& orig, Args&&... args) ->
    decltype(genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...)))
  {
    return genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...));
  }

  //! \c TypeEvaluator to turn a type \c T into a reference to \c T
  /**
   * This is suitable as the \c TypeEvaluator template parameter for
   * ForEachType and transformTuple().
   */
  template<class T>
  struct AddRefTypeEvaluator
  {
    typedef T& Type;
    static Type apply(T& t)
    {
      return t;
    }
  };

  //! \c TypeEvaluator to turn a type \c T into a pointer to \c T
  /**
   * This is suitable as the \c TypeEvaluator template parameter for
   * ForEachType and transformTuple().
   */
  template<class T>
  struct AddPtrTypeEvaluator
  {
    typedef typename remove_reference<T>::type* Type;
    static Type apply(T& t)
    {
      return &t;
    }
  };

  // Specialization, in case the type is already a reference
  template<class T>
  struct AddPtrTypeEvaluator<T&>
  {
    typedef typename std::remove_reference<T>::type* Type;
    static Type apply(T& t)
    {
      return &t;
    }
  };

  namespace
  {
    template<int i, typename T1,typename F>
    struct Visitor
    {
      static inline void visit(F& func, T1& t1)
      {
        func.visit(std::get<std::tuple_size<T1>::value-i>(t1));
        Visitor<i-1,T1,F>::visit(func, t1);
      }
    };

    template<typename T1,typename F>
    struct Visitor<0,T1,F>
    {
      static inline void visit(F&, T1&)
      {}
    };

    template<int i, typename T1, typename T2,typename F>
    struct PairVisitor
    {
      static inline void visit(F& func, T1& t1, T2& t2)
      {
        func.visit(std::get<std::tuple_size<T1>::value-i>(t1), std::get<std::tuple_size<T2>::value-i>(t2));
        PairVisitor<i-1,T1,T2,F>::visit(func, t1, t2);
      }
    };

    template<typename T1, typename T2, typename F>
    struct PairVisitor<0,T1,T2,F>
    {
      static inline void visit(F&, T1&, T2&)
      {}
    };
  }

  /**
   * @brief Helper template which implements iteration over all storage
   * elements in a std::tuple.
   *
   * Compile-time constructs that allows one to process all elements in a std::tuple.
   * The exact operation performed on an element is defined by a function
   * object, which needs to implement a visit method which is applicable to
   * all storage elements of a std::tuple.  Each std::tuple element is visited once, and
   * the iteration is done in ascending order.
   *
   * The following example implements a function object which counts the
   * elements in a std::tuple
   *
   * \code
   * template <class T>
   * struct Counter
   * {
   *   Counter() :
   *     result_(0)
   *   {}
   *
   *   template <class T>
   *   void visit(T& elem)
   *   {
   *     ++result_;
   *   }
   *
   *   int result_;
   * };
   * \endcode
   *
   * The number of elements in the std::tuple are stored in the member variable
   * result_. The Counter can be used as follows, assuming a std::tuple t of type
   * MyTuple is given:
   *
   * \code
   * Counter c;
   * ForEachValue<MyTuple> forEach(t);
   *
   * forEach.apply(c);
   * std::cout << "Number of elements is: " << c.result_ << std::endl;
   * \endcode
   */
  template<class Tuple>
  class ForEachValue
  {
  public:
    //! \brief Constructor
    //! \param t The std::tuple which we want to process.
    ForEachValue(Tuple& t) :
      t_(t)
    {}

    //! \brief Applies a function object to each storage element of the std::tuple.
    //! \param f Function object.
    template<class Functor>
    void apply(Functor& f) const
    {
      Visitor<std::tuple_size<Tuple>::value,Tuple,Functor>::visit(f, t_);
    }
  private:
    Tuple& t_;
  };

  /**
   * @brief Extension of ForEachValue to two std::tuple's.
   *
   * This class provides the framework to process two std::tuple's at once. It works
   * the same as ForEachValue, just that the corresponding function object
   * takes one argument from the first std::tuple and one argument from the second.
   *
   * \note You have to ensure that the two std::tuples you provide are compatible
   * in the sense that they have the same length and that the objects passed
   * to the function objects are related in meaningful way. The best way to
   * enforce it is to build the second std::tuple from the existing first std::tuple
   * using ForEachType.
   */
  template<class Tuple1, class Tuple2>
  class ForEachValuePair
  {
  public:
    //! Constructor
    //! \param t1 First std::tuple.
    //! \param t2 Second std::tuple.
    ForEachValuePair(Tuple1& t1, Tuple2& t2) :
      t1_(t1),
      t2_(t2)
    {}

    //! Applies the function object f to the pair of std::tuple's.
    //! \param f The function object to apply on the pair of std::tuple's.
    template<class Functor>
    void apply(Functor& f)
    {
      PairVisitor<std::tuple_size<Tuple1>::value,Tuple1,Tuple2,Functor>::visit(f, t1_, t2_);
    }
  private:
    Tuple1& t1_;
    Tuple2& t2_;
  };

  /**
   * @brief Type for reverse element access.
   *
   * Counterpart to ElementType for reverse element access.
   */
  template<int N, class Tuple>
  struct AtType
  {
    typedef typename std::tuple_element<std::tuple_size<Tuple>::value - N - 1, Tuple>::type Type;
  };

  /**
   * @brief Reverse element access.
   *
   * While Element<...> gives you the arguments beginning at the front of a
   * std::tuple, At<...> starts at the end, which may be more convenient, depending
   * on how you built your std::tuple.
   */
  template<int N>
  struct At
  {
    template<typename Tuple>
    static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::NonConstType
    get(Tuple& t)
    {
      return std::get<std::tuple_size<Tuple>::value - N - 1>(t);
    }

    template<typename Tuple>
    static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::ConstType
    get(const Tuple& t)
    {
      return std::get<std::tuple_size<Tuple>::value - N - 1>(t);
    }
  };

  /**
   * @brief Deletes all objects pointed to in a std::tuple of pointers.
   *
   * \warning Pointers cannot be set to nullptr, so calling the Deletor twice
   * or accessing elements of a deleted std::tuple leads to unforeseeable results!
   */
  template<class Tuple>
  class PointerPairDeletor
  {
    struct Deletor
    {
      template<typename P>
      void visit(const P& p)
      {
        delete p;
      }
    };

  public:
    static void apply(Tuple& t)
    {
      static Deletor deletor;
      ForEachValue<Tuple>(t).apply(deletor);
    }
  };

  /**
   * @brief Finding the index of a certain type in a std::tuple
   *
   * \tparam Tuple     The std::tuple type to search in.
   * \tparam Predicate Predicate which tells FirstPredicateIndex which types
   *                   in Tuple to accept.  This should be a class template
   *                   taking a single type template argument.  When
   *                   instantiated, it should contain a static member
   *                   constant \c value which should be convertible to bool.
   *                   A type is accepted if \c value is \c true, otherwise it
   *                   is rejected and the next type is tried.  Look at IsType
   *                   for a sample implementation.
   * \tparam start     First index to try.  This can be adjusted to skip
   *                   leading tuple elements.
   * \tparam size      This parameter is an implementation detail and should
   *                   not be adjusted by the users of this class.  It should
   *                   always be equal to the size of the std::tuple.
   *
   * This class can search for a type in std::tuple. It will apply the predicate
   * to each type in std::tuple in turn, and set its member constant \c value to
   * the index of the first type that was accepted by the predicate.  If none
   * of the types are accepted by the predicate, a static_assert is triggered.
   */
  template<class Tuple, template<class> class Predicate, std::size_t start = 0,
      std::size_t size = std::tuple_size<Tuple>::value>
  class FirstPredicateIndex :
    public std::conditional<Predicate<typename std::tuple_element<start,
                Tuple>::type>::value,
        std::integral_constant<std::size_t, start>,
        FirstPredicateIndex<Tuple, Predicate, start+1> >::type
  {
    static_assert(std::tuple_size<Tuple>::value == size, "The \"size\" "
                       "template parameter of FirstPredicateIndex is an "
                       "implementation detail and should never be set "
                       "explicitly!");
  };

#ifndef DOXYGEN
  template<class Tuple, template<class> class Predicate, std::size_t size>
  class FirstPredicateIndex<Tuple, Predicate, size, size>
  {
    static_assert(Std::to_false_type<Tuple>::value, "None of the std::tuple element "
                       "types matches the predicate!");
  };
#endif // !DOXYGEN

  /**
   * @brief Generator for predicates accepting one particular type
   *
   * \tparam T The type to accept.
   *
   * The generated predicate class is useful together with
   * FirstPredicateIndex.  It will accept exactly the type that is given as
   * the \c T template parameter.
   */
  template<class T>
  struct IsType
  {
    //! @brief The actual predicate
    template<class U>
    struct Predicate : public std::is_same<T, U> {};
  };

  /**
   * @brief Find the first occurance of a type in a std::tuple
   *
   * \tparam Tuple The std::tuple type to search in.
   * \tparam T     Type to search for.
   * \tparam start First index to try.  This can be adjusted to skip leading
   *               std::tuple elements.
   *
   * This class can search for a particular type in std::tuple. It will check each
   * type in the std::tuple in turn, and set its member constant \c value to the
   * index of the first occurance of type was found.  If the type was not
   * found, a static_assert is triggered.
   */
  template<class Tuple, class T, std::size_t start = 0>
  struct FirstTypeIndex :
    public FirstPredicateIndex<Tuple, IsType<T>::template Predicate, start>
  { };

  /**
   * \brief Helper template to append a type to a std::tuple
   *
   * \tparam Tuple The std::tuple type to extend
   * \tparam T     The type to be appended to the std::tuple
   */
  template<class Tuple, class T>
  struct PushBackTuple;

  template<class... Args, class T>
  struct PushBackTuple<typename std::tuple<Args...>, T>
  {
    typedef typename std::tuple<Args..., T> type;
  };

  /**
   * \brief Helper template to prepend a type to a std::tuple
   *
   * \tparam Tuple The std::tuple type to extend
   * \tparam T     The type to be prepended to the std::tuple
   */
  template<class Tuple, class T>
  struct PushFrontTuple;

  template<class... Args, class T>
  struct PushFrontTuple<typename std::tuple<Args...>, T>
  {
    typedef typename std::tuple<T, Args...> type;
  };

  /**
   * \brief Apply reduce with meta binary function to template
   *
   * For a tuple\<T0,T1,...,TN-1,TN,...\> the exported result is
   *
   * F\< ... F\< F\< F\<Seed,T0\>\::type, T1\>\::type, T2\>\::type,  ... TN-1\>\::type
   *
   * \tparam F Binary meta function
   * \tparam Tuple Apply reduce operation to this std::tuple
   * \tparam Seed Initial value for reduce operation
   * \tparam N Reduce the first N std::tuple elements
   */
  template<
      template <class, class> class F,
      class Tuple,
      class Seed=std::tuple<>,
      int N=tuple_size<Tuple>::value>
  struct ReduceTuple
  {
    typedef typename ReduceTuple<F, Tuple, Seed, N-1>::type Accumulated;
    typedef typename std::tuple_element<N-1, Tuple>::type Value;

    //! Result of the reduce operation
    typedef typename F<Accumulated, Value>::type type;
  };

  /**
   * \brief Apply reduce with meta binary function to template
   *
   * Specialization for reduction of 0 elements.
   * The exported result type is Seed.
   *
   * \tparam F Binary meta function
   * \tparam Tuple Apply reduce operation to this std::tuple
   * \tparam Seed Initial value for reduce operation
   */
  template<
      template <class, class> class F,
      class Tuple,
      class Seed>
  struct ReduceTuple<F, Tuple, Seed, 0>
  {
    //! Result of the reduce operation
    typedef Seed type;
  };

  /**
   * \brief Join two std::tuple's
   *
   * For Head=std::tuple<T0,...,TN> and Tail=std::tuple<S0,...,SM>
   * the exported result is std::tuple<T0,..,TN,S0,...,SM>.
   *
   * \tparam Head Head of resulting std::tuple
   * \tparam Tail Tail of resulting std::tuple
   */
  template<class Head, class Tail>
  struct JoinTuples
  {
    //! Result of the join operation
    typedef typename ReduceTuple<PushBackTuple, Tail, Head>::type type;
  };

  /**
   * \brief Flatten a std::tuple of std::tuple's
   *
   * This flattens a std::tuple of tuples std::tuple<std::tuple<T0,...,TN>, std::tuple<S0,...,SM> >
   * and exports std::tuple<T0,..,TN,S0,...,SM>.
   *
   * \tparam TupleTuple A std::tuple of std::tuple's
   */
  template<class Tuple>
  struct FlattenTuple
  {
    //! Result of the flatten operation
    typedef typename ReduceTuple<JoinTuples, Tuple>::type type;
  };

}

#endif