Partage
  • Partager sur Facebook
  • Partager sur Twitter

Fonction variadique comme argument template

Un peu d'enfer

Sujet résolu
    10 novembre 2014 à 19:05:10

    Bonjour.

    Je suis en train de coder un wrapper pour sqlite afin de simplifier l'utilisation de libsqlite3 qui est un vrai enfer. J'arrive a lancer des requêtes et récupérer des données, mais j'aimerais étendre ce que j'ai fait pour pouvoir appliquer une fonction a chaque ligne de donnée récupérée.

    Voila la version simplifiée de mon problème (on suppose que je récupère qu'une ligne de ma table à la fois)

    J'ai une fonction get_data qui marche, et qui me permet d'extraire des données et de les ranger dans un tuple. Derrière il y a un peu de magie, pour apperler les bonnes fonction sqlite en fonction du type template, et pour cracher des erreurs si le type reçu n'est pas celui du template.

    template<typename... Args>
    std::tuple<Args...> get_data(const std::string & sql){
    	std::tuple<Args...>  R;
    	//des trucs magiques
    	return R;
    }

     Cette fonction est pratique, je peux par exemple écrire.

    std::tuple<int,std::string> age_pers=get_data<int,std::string>("select age,nom from personne limit 1");
    
    auto age_pers2 = get_data<int,std::string>("select age,nom from personne limit 1");

    J'aimerais ensuite écrire du code générique qui applique a la volée (c'est a dire sans tout stocker dans un container) une fonction utilisateur aux données renvoyées par get_data. Ceci pose trois probmèles:
    - J'écris quoi dans les <> du template pour passer une fonction qui marche avec les fonctions libres, les foncteurs, les lambdas et tout les trucs aux quel je n'ai pas pensé qui s'appellent comme des fonctions?
    - Comment je récupère le type des arguments de la fonction que j'ai passé pour le donner à get_data
    - get_data renvoie un tuple, est ce que je peux le "déplier" pour qu'il ressemble a un paquet d'arguments classiques? (si non, existe t'il une solution qui suppose que la fonction utilisateur prend un tuple de n'importe quoi?.

    //j'ai par exemple
    bool f(int i){std::cout << "f "<<i<<"\n";}
    auto lambda=[](std::string s, int i){std::cout << "lambda" <<s <<" "<<i<<"\n";}
    struct fonct{bool operator()(int i){std::cout << "fonct "<<i<<"\n";}
    
    
    //je voudrai pouvoir faire
    apply("select age from personne",f1);
    apply("select nom,age from personne",lambda);
    apply("select age from personne",fonct );


    Voila ma proposition, qui ne marche pas

    //Je galère pour écrire le code template qui fait ca
    template<bool Fn(Args...)>// <-- je met quoi ici?
    void apply(const std::string & sql, Fn & function){
    	while(have_data()){
    		const auto &tuple_data = get_data<Args...>(); //<-- idem, comment je récupère la liste des types à passer.
    		bool ok = Fn(/*???*/);//<-- je passe quoi?
    		if(!ok){return false;}
    	}
    	return true;
    }



    • Partager sur Facebook
    • Partager sur Twitter
      10 novembre 2014 à 20:17:58

      Ta fonction template va remplacer le template par son type, même si c'est une fonction. Regarde le prototype de for_each :

      template< class InputIt, class UnaryFunction >
      UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

      http://en.cppreference.com/w/cpp/algorithm/for_each

      -
      Edité par anolya 10 novembre 2014 à 20:19:18

      • Partager sur Facebook
      • Partager sur Twitter
        11 novembre 2014 à 1:34:15

        Compatibilité c++14/c++11 ()

        #include <type_traits>
        #include <utility>
        
        #if __cplusplus == 201103L
        namespace std {
        template<class T, T... Ints>
        struct integer_sequence
        {
          using value_type = T;
          static constexpr size_t size() noexcept
          { return sizeof...(Ints); }
        };
        
        template<size_t... Ints>
        using index_sequence = integer_sequence<std::size_t, Ints...>;
        
        namespace aux_ {
        template<class T, T... I>
        struct index_sequence
        {
          typedef index_sequence<T, I..., T(sizeof...(I))> _next;
          typedef ::std::integer_sequence<T, I...> _sequence;
        };
        
        template<class T, T E, T N>
        struct build_integer_sequence
        {
          typedef typename build_integer_sequence<T, E, N-1>::_type::_next _type;
        };
        
        template<class T, T E>
        struct build_integer_sequence<T, E, E>
        {
          typedef index_sequence<T> _type;
        };
        }
        
        template<class T, T N>
        using make_integer_sequence = typename aux_::build_integer_sequence<
        T, T(0), N>::_type::_sequence;
        
        template<size_t N>
        using make_index_sequence = make_integer_sequence<size_t, N>;
        }
        #endif
        

        apply_from_tuple (c++14 ?)

        template<class F, class Tuple, std::size_t... I>
        bool apply_from_tuple(std::integer_sequence<std::size_t, I...>, F f, Tuple&& tuple)
        { return f(std::get<I>(std::forward<Tuple>(tuple))...); }
        

        template<class F, class Tuple> bool apply_from_tuple(F f, Tuple&& tuple) { return apply_from_tuple(

        std::make_index_sequence&lt;std::tuple_size&lt;
          typename std::remove_reference&lt;Tuple>::type
        >::value>()
        

        , f, std::forward<Tuple>(tuple)); }

        </pre>

        Récupérer un tuple de paramètres

        template<class R, class C, class... Args>
        std::tuple<Args...> param_objet(R (C::*)(Args...));
        

        template<class R, class C, class... Args> std::tuple<Args...> param_objet(R (C::*)(Args...) const);

        template<class T> struct get_tuple_params { typedef decltype(param_objet(&T::operator())) type; };

        template<class R, class... Args> struct get_tuple_params<R(*)(Args...)> { typedef std::tuple<Args...> type; };

        </pre> (Je pense qu'il faut faire remplacer les std::tuple<Args...> par std::tuple<typename std::remove_reference<Args>::type...>)

        apply

        template<class Tuple>
        struct dispatch_get_data;
        

        template<class... Args> std::tuple<Args...> get_data() { return std::tuple<Args...>(); }

        template<class... Args> struct dispatch_get_data<std::tuple<Args...>> { static auto impl() -> decltype(get_data<Args...>()) { return get_data<Args...>(); } };

        bool have_data() {return true; }

        template<class F> bool apply(const std::string & sql, F function){ while(have_data()){

        using Tuple = typename get_tuple_params&lt;F>::type;
        const auto &tuple_data = dispatch_get_data&lt;Tuple>::impl();
        // c++17 ? std::apply_from_tuple
        bool ok = apply_from_tuple(function, tuple_data);
        if(!ok){return false;}
        

        } return true; }

        </pre>

        tests

        bool f(int i){std::cout << "f "<<i<<"\n"; return true; }
        auto lambda=[](std::string s, int i) -> bool {std::cout << "lambda" <<s <<" "<<i<<"\n"; return true; };
        struct fonct{bool operator()(int i) /const/ {std::cout << "fonct "<<i<<"\n"; return true; } };
        

        int main(int, char **) { apply("select age from personne", f); apply("select nom,age from personne", lambda); apply("select age from personne", fonct()); }

        </pre>
        • Partager sur Facebook
        • Partager sur Twitter
          11 novembre 2014 à 3:10:56

          Heu, y-a toujours pas d'ORM en C++ ???
          • Partager sur Facebook
          • Partager sur Twitter
          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
            12 novembre 2014 à 9:47:27

            @bacelar
            Mon problème pratique est de m'interfacer avec des bases existantes que j'utilise aussi dans d'autres contextes avec d'autres programmes (pour du data mining essentiellement), pas de sauvegarder mes objets dans une base.

            @anolya
            Je sais faire passer une fonction, là où je sèche, c'est pour récupérer les arguments de la fonction, et construire un tuple avec

            @jo_link_noir
            J'ai pas compris comment ta solution marche. Est ce que tu peux m'expliquer s'il te plait. Merci pour la proposition en tout cas.  Je compte utiliser ton code pour une lib, est ce que je peux, et est ce que tu veux que je te rajoute aux crédits (si oui, a quel nom?).

            J'au aussi tenté de généraliser la structure dispatch_get_data. Voila mon code, j'ai une erreur ligne 10 et je ne comprend pas pourquoi.

            //remplace dispach_get_data
            template<class T> struct tuple_dispatcher{};
            
            template<class... Args>
            struct tuple_dispatcher<std::tuple<Args... >>{
            
            	template<typename GetData, typename... Fn_args>
            	static auto run(GetData &gd , Fn_args... fn_args) -> std::tuple<Args...>
            	{
            		 return  gd.get_data<Args...>(fn_args...); //ERREUR ICI : expected primary expression before ...
            	}
            
            };
            
            
            template<class F,  class Tuple_getData_t, typename... getData_args>
            bool tuple_function(
            		F function,
            		Tuple_getData_t &gd,
            		getData_args... gd_args
            		){
            
            
              while(gd.have_data()){
                using Tuple_type       = typename get_tuple_params<F>::type;
                const auto &tuple_data = tuple_dispatcher<Tuple_type>::run(gd,gd_args...);
                bool ok = apply_from_tuple(function, tuple_data);
                if(!ok){return false;}
              }
              return true;
            }

             avec pour code de test

            //template<typename... Fn_args>
            struct Tuple_function_getData{
            	bool ok=true;
            	template<typename... Args> std::tuple<Args...> get_data(const std::string){return std::tuple<Args...>();}
            	bool have_data(){if(ok){ok=false;return true;}return false;};
            };
            
            
            bool f(int i){std::cout << "f "<<i<<"\n"; return true; }
            auto lambda=[](std::string s, int i) -> bool {std::cout << "lambda" <<s <<" "<<i<<"\n"; return true; };
            struct fonct{bool operator()(int i) {std::cout << "fonct "<<i<<"\n"; return true; } };
            
            void ttest()
            {
              Tuple_function_getData gd;
              tuple_function(f       ,gd,"select age from personne" );
              tuple_function(lambda  ,gd,"select nom,age from personne" );
              tuple_function(fonct() ,gd,"select age from personne");
            }
            



            -
            Edité par ledemonboiteux 12 novembre 2014 à 13:34:26

            • Partager sur Facebook
            • Partager sur Twitter
              12 novembre 2014 à 18:26:53

              Pour integer_sequence, make_index_sequence et make_integer_sequence je te renvois à la doc: http://en.cppreference.com/w/cpp/utility/integer_sequence

              Pour apply_from_tuple, ce papier: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3829.pdf apply_from_tuple prend un tuple (t), un foncteur (f) et optionnellement les indices du tuple à transmettre à la fonction. Si les indices ne sont pas donnés le tuple est entièrement déballé et transmit à f (f(get<0>(t), std::get<1>(t), std::get<N-1>(t)) avec N le nombre d'élément dans le tuple).

              param_objet prend un pointeur sur fonction membre et en extrait les paramètres (seule la déclaration importe car la fonction est uniquement utilisée avec decltype). get_tuple_params propage operator() à pram_objet (pour les classes et lambda) et si le type est un pointeur sur fonction en déduit directement les paramètres. Note que param_objet ne prend pas en compte les fonctions membres volatiles.

              Pour ton erreur je pense qu'il faut faire gd.template get_data<Args...>(fn_args...).

              • Partager sur Facebook
              • Partager sur Twitter
                13 novembre 2014 à 12:47:06

                @jo_link_noir.

                Merci beaucoup pour ton aide, le fix que tu as proposé m'a permis de faire marcher mon code.


                Edit : le code qui marche est ici avec sa doc
                https://tentacule.be/fossil/cpp-sql_wrapper/

                -
                Edité par ledemonboiteux 13 novembre 2014 à 19:40:08

                • Partager sur Facebook
                • Partager sur Twitter

                Fonction variadique comme argument template

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown