web-dev-qa-db-fra.com

Template Tuple - appel d'une fonction sur chaque élément

Ma question est dans le code:

template<typename... Ts>
struct TupleOfVectors {
  std::Tuple<std::vector<Ts>...> Tuple;

  void do_something_to_each_vec() {
    //Question: I want to do this:
    //  "for each (N)": do_something_to_vec<N>()
    //How?
  }

  template<size_t N>
  void do_something_to_vec() {
    auto &vec = std::get<N>(Tuple);
    //do something to vec
  }
};
40
7cows

Vous pouvez le faire assez facilement avec certains mécanismes d'indices. Étant donné une méta-fonction gen_seq pour générer des séquences entières au moment de la compilation (encapsulées par le modèle de classe seq):

namespace detail
{
    template<int... Is>
    struct seq { };

    template<int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

    template<int... Is>
    struct gen_seq<0, Is...> : seq<Is...> { };
}

Et les modèles de fonctions suivants:

#include <Tuple>

namespace detail
{
    template<typename T, typename F, int... Is>
    void for_each(T&& t, F f, seq<Is...>)
    {
        auto l = { (f(std::get<Is>(t)), 0)... };
    }
}

template<typename... Ts, typename F>
void for_each_in_Tuple(std::Tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
}

Vous pouvez utiliser le for_each_in_Tuple fonction au-dessus de cette façon:

#include <string>
#include <iostream>

struct my_functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    std::Tuple<int, double, std::string> t(42, 3.14, "Hello World!");
    for_each_in_Tuple(t, my_functor());
}

Voici un ( exemple en direct .

Dans votre situation concrète, voici comment vous pouvez l'utiliser:

template<typename... Ts>
struct TupleOfVectors
{
    std::Tuple<std::vector<Ts>...> t;

    void do_something_to_each_vec()
    {
        for_each_in_Tuple(t, Tuple_vector_functor());
    }

    struct Tuple_vector_functor
    {
        template<typename T>
        void operator () (T const &v)
        {
            // Do something on the argument vector...
        }
    };
};

Et encore une fois, voici un exemple en direct .

34
Andy Prowl

En C++ 17, vous pouvez faire ceci:

std::apply([](auto ...x){std::make_Tuple(some_function(x)...);} , the_Tuple);

étant donné que some_function a des surcharges appropriées pour tous les types du Tuple.

Cela fonctionne déjà dans Clang ++ 3.9, en utilisant std::experimental::apply.

32
M. Alaggan

En plus de la réponse de @M. Alaggan, si vous avez besoin d'appeler une fonction sur des éléments Tuple par ordre d'apparition dans le Tuple, en C++ 17, vous pouvez également utiliser une expression de repli comme celle-ci:

std::apply([](auto& ...x){(..., some_function(x));}, the_Tuple);

( exemple en direct ).

Parce que sinon l'ordre d'évaluation des arguments de la fonction n'est pas spécifié .

18
Dev Null

Voici une approche qui peut bien fonctionner dans votre cas:

template<typename... Ts>
struct TupleOfVectors {
    std::Tuple<std::vector<Ts>...> Tuple;

    void do_something_to_each_vec()
    {
        // First template parameter is just a dummy.
        do_something_to_each_vec_helper<0,Ts...>();
    }

    template<size_t N>
    void do_something_to_vec()
    {
        auto &vec = std::get<N>(Tuple);
        //do something to vec
    }

private:
    // Anchor for the recursion
    template <int>
    void do_something_to_each_vec_helper() { }

    // Execute the function for each template argument.
    template <int,typename Arg,typename...Args>
    void do_something_to_each_vec_helper()
    {
        do_something_to_each_vec_helper<0,Args...>();
        do_something_to_vec<sizeof...(Args)>();
    }
};

La seule chose qui est un peu désordonnée ici est le paramètre de modèle int extra factice pour do_something_to_each_vec_helper. Il est nécessaire de faire de do_something_to_each_vec_helper un modèle lorsqu'il ne reste aucun argument. Si vous vouliez utiliser un autre paramètre de modèle, vous pouvez l’utiliser à la place.

7
Vaughn Cato

Si vous n'êtes pas particulièrement lié à une solution sous la forme d'un modèle de fonction générique "pour chaque", vous pouvez en utiliser un comme celui-ci:

#ifndef Tuple_OF_VECTORS_H
#define Tuple_OF_VECTORS_H

#include <vector>
#include <Tuple>
#include <iostream>

template<typename... Ts>
struct TupleOfVectors 
{
    std::Tuple<std::vector<Ts>...> Tuple;

    template<typename ...Args>
    TupleOfVectors(Args... args)
    : Tuple(args...){}

    void do_something_to_each_vec() {
        do_something_to_vec(Tuple);
    }

    template<size_t I = 0, class ...P>
    typename std::enable_if<I == sizeof...(P)>::type
    do_something_to_vec(std::Tuple<P...> &) {}

    template<size_t I = 0, class ...P>
    typename std::enable_if<I < sizeof...(P)>::type
    do_something_to_vec(std::Tuple<P...> & parts) {
        auto & part = std::get<I>(Tuple);
        // Doing something...
        std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
        do_something_to_vec<I + 1>(parts);
    }
};

#endif // EOF

Un programme de test, construit avec GCC 4.7.2 et clang 3.2:

#include "Tuple_of_vectors.h"

using namespace std;

int main()
{
    TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
        vector<int>(2,2),
        vector<int>(3,3),
        vector<int>(4,4));

    vecs.do_something_to_each_vec();
    return 0;
}

Le même style de récursivité peut être utilisé dans un modèle de fonction générique "for_each" sans appareil d'indices auxiliaires:

#ifndef FOR_EACH_IN_Tuple_H
#define FOR_EACH_IN_Tuple_H

#include <type_traits>
#include <Tuple>
#include <cstddef>

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_Tuple(std::Tuple<Ts...> &, Func) {}

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_Tuple(std::Tuple<Ts...> & tpl, Func func) 
{
    func(std::get<I>(tpl));
    for_each_in_Tuple<I + 1>(tpl,func);
}

#endif //EOF

Et un programme de test pour ça:

#include "for_each_in_Tuple.h"
#include <iostream>

struct functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    auto tpl = std::make_Tuple(1,2.0,"Three");
    for_each_in_Tuple(tpl,functor());
    return 0;
}
6
Mike Kinghan

Je testais avec des tuples et de la métaprogrammation et j'ai trouvé le thread actuel. Je pense que mon travail peut inspirer quelqu'un d'autre bien que j'aime la solution de @Andy.

Quoi qu'il en soit, amusez-vous!

#include <Tuple>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <functional>

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I != std::Tuple_size<Tuple>::value, void >::type
for_each(const Tuple& Tuple, Func&& func)
{
    func(std::get<I>(Tuple));
    for_each<I + 1>(Tuple, func);
}

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I == std::Tuple_size<Tuple>::value, void >::type
for_each(const Tuple& Tuple, Func&& func)
{
    // do nothing
}


struct print
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

template<typename... Params>
void test(Params&& ... params)
{
    int sz = sizeof...(params);
    std::Tuple<Params...> values(std::forward<Params>(params)...);
    for_each(values, print() );
}


class MyClass
{
public:
    MyClass(const std::string& text) 
        : m_text(text)
    {
    }

    friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
    {
        stream << myClass.m_text;
        return stream;
    }

private:
    std::string m_text;
};


int main()
{
    test(1, "hello", 3.f, 4, MyClass("I don't care") );
}
2
dmayola