IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Variadic templates et std::function


Sujet :

C++

  1. #21
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Voila où j'en suis arrivé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #include<utility>
     
    #include<boost/mpl/bool.hpp>
     
    namespace mpl = boost::mpl;
     
    template<class Fun, class... Arg>
    mpl::true_ callable(decltype(std::declval<Fun>()(std::declval<Arg>()...))*);
     
    template<class Fun, class... Arg>
    mpl::false_ callable(...);
    Permet de faire du SFINAE pour savoir si un type supporte un appel ou pas, couplé avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    #include<boost/mpl/equal_to.hpp>
    #include<boost/mpl/eval_if.hpp>
    #include<boost/mpl/push_back.hpp>
    #include<boost/mpl/size_t.hpp>
    #include<boost/mpl/vector_c.hpp>
     
    struct any
    {
    	template<class T>
    	operator T() const;
    };
     
    template<class Fun, class Seq =mpl::vector_c<size_t>, class... Arg>
    struct arity : mpl::eval_if<
    	mpl::equal_to<
    		mpl::size_t<sizeof...(Arg)>,
    		mpl::size_t<9>
    	>,
    	Seq,
    	arity<
    		Fun,
    		typename mpl::eval_if<
    			decltype(callable<Fun,Arg...>(0)),
    			mpl::push_back<
    				Seq,
    				mpl::size_t<sizeof...(Arg)>
    			>,
    			Seq
    		>::type,
    		any,Arg...
    	>
    >::type {};
    C'est une méta-fonction (convention de notation boost.mpl) qui permet de déterminer les arités d'un foncteur "presque" quelconque (il faut néanmoins le type de ce foncteur, ce qu'on a généralement si on effectue bien les passages de foncteur par template). Ensuite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #include<iterator>
    #include<type_traits>
     
    template<class Fun, class Seq, size_t... Ns>
    void call(Fun&& fun, const Seq& seq, const std::index_sequence<Ns...>&)
    {
    	using std::begin;
    	using std::next;
     
    	std::forward<Fun>(fun)((*next(begin(seq),Ns))...);
    }
    Fonction call pour forwarder l'appel (voir code de jo_link_noir)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
     
    #include<functional>
     
    #include<boost/mpl/for_each.hpp>
    #include<boost/range/size.hpp>
     
    template<class Fun, class Seq>
    struct try_call
    {
    	try_call(Fun&& fun_, const Seq& seq_, bool& res_)
    		: res(res_)
    		, fun(std::forward<Fun>(fun_))
    		, seq(seq_)
    	{}
     
    	template<class T>
    	void operator()(const T&)
    	{
    		using boost::size;
     
    		constexpr auto checked_size = T::value;
     
    		if(size(seq) == checked_size)
    		{
    			call(fun,seq,std::make_index_sequence<checked_size>());
    			res=true;
    		}
    	}
     
    	bool has_call() const
    	{ return res; }
     
    private:
    	bool& res;
    	Fun fun;
    	const Seq& seq;
    };
     
    template<class Fun, class Seq>
    try_call<Fun,Seq> make_try(Fun&& fun, const Seq& seq, bool& res)
    { return try_call<Fun,Seq>(std::forward<Fun>(fun),seq,res); }
     
    template<class Seq, class Fun>
    std::function<void(const Seq&)>
    	make_adapt(Fun&& fun)
    {
    	return
    		[fun=fun](const Seq& seq) mutable
    		{
    			bool has_call = false;
    			auto try_object = make_try(fun,seq,has_call);
     
    			mpl::for_each<typename arity<Fun>::type>
    				(try_object);
     
    			if(!has_call)
    				throw "what ever";
    		}
    	;
    }
    Ce code a besoin d'un paramètre template qui est le type de la séquence des paramètres que tu utilises pour les appels. Il prend un foncteur monomorphique quelconque et retourne un autre foncteur qui prend en paramètre une séquence du type mentionné plus haut et qui fera suivre les paramètres au premier foncteur. Dans ton exemple plus haut, si j'ai suivi, le type de la séquence est une map sur des variants. Je n'ai pas les détails sur ton variant et les conversions qu'il peut faire, j'ai donc testé dans un cas sans aucune conversion avec des int partout :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
     
    #include<iostream>
    #include<vector>
     
    void foo(int i){ std::cout << "foo" << i; }
     
    template<class T>
    void bar(T i, T j){ std::cout << "bar" << i << j; }
     
    struct goo
    {
    	void operator()(int i, int j, int k =0) const
    	{ std::cout << "goo" << i << j << k; }
    };
     
    struct hoo
    {
    	template<class T>
    	void operator()(T i, T j, T k, T l) const
    	{ std::cout << "hoo" << i << j << k << l; }
    };
     
    int main()
    {
    	using param_type = std::vector<int>;
     
    	param_type seq_param;
    	seq_param.reserve(5);
     
    	std::vector<
    		std::function<void(const std::vector<int>&)>
    	> seq_fun;
    	seq_fun.reserve(5);
     
    	seq_fun.push_back(make_adapt<param_type>(foo));
    	seq_fun.push_back(make_adapt<param_type>(&bar<int>));
    	seq_fun.push_back(make_adapt<param_type>(goo()));
    	seq_fun.push_back(make_adapt<param_type>(hoo()));
    	seq_fun.push_back(make_adapt<param_type>(
    		[](int i, int j, int k, int l, int m)
    		{ std::cout << "ffun" << i << j << k << l << m; }
    	));
     
    	seq_param.push_back(1);
    	seq_fun[0](seq_param);
     
    	seq_param.push_back(2);
    	seq_fun[1](seq_param);
     
    	seq_fun[2](seq_param);
    	seq_param.push_back(3);
    	seq_fun[2](seq_param);
     
    	seq_param.push_back(4);
    	seq_fun[3](seq_param);
     
    	seq_param.push_back(5);
    	seq_fun[4](seq_param);
     
    	seq_fun[0](seq_param);
    }
    Edit: Quelques changement, en partie inspiré par jblecanard
    Edit2: Gros changement suite aux messages de joe_link_noir
    Edit3: Gros changement à nouveau, fonctionnement avec des paramètres par défaut

  2. #22
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    @White_Tentacle : très bien le template récursif, j'aurais fait un truc du genre aussi. Par contre, j'ai une préférence pour des fonctions libres plutôt que des membres statiques. Voir l'exemple ci-après.

    @Flob90 : J'aime bien la généricité de ton approche. Mais on peut se passer de boost.

    En combinant nos trois contributions, on peut obtenir ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    #include <iostream>
    #include <array>
    #include <type_traits>
    #include <functional>
    #include <vector>
     
    // make_function based on jblecanard
    template <typename ReturnType, typename... Args> struct base_traits {
      using return_type = ReturnType;
      using function = const std::function<ReturnType(Args...)>;
      static size_t constexpr arity = sizeof ... (Args); 
    };
    template <typename Function> struct function_traits;
    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(Args...)> : public base_traits<ReturnType, Args...> {};
    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(&)(Args...)> : public base_traits<ReturnType, Args...> {};
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const> : public base_traits<ReturnType, Args...> {};
    template <typename ReturnType, typename... Args>
    struct function_traits<std::function<ReturnType(Args...)>> : public base_traits<ReturnType, Args...> {};
     
    template<typename T>
    auto make_function(T const& f) ->
    typename std::enable_if<std::is_function<T>::value && !std::is_bind_expression<T>::value, std::function<T>>::type
    { return f; }
     
    template<typename T>
    auto make_function(T const& f) ->
    typename std::enable_if<!std::is_function<T>::value && !std::is_bind_expression<T>::value, typename function_traits<decltype(&T::operator())>::function>::type
    { return static_cast<typename function_traits<decltype(&T::operator())>::function>(f); }
     
     
    // call based on WHite_Tentacle
    template <typename ... Args> struct size_traits;
    template <typename ... Args> struct size_traits<std::tuple<Args...>> { static size_t constexpr size = sizeof ... (Args); };
    template <typename ValueType, size_t Size> struct size_traits<std::array<ValueType, Size>> { static size_t constexpr size = Size; };
     
    template<size_t NbRemaining, typename Fun, typename Array, typename ... Args>
    inline auto private_call(Fun const& fun, Array const&, Args ... args) ->
    typename std::enable_if<0u == NbRemaining, typename function_traits<Fun>::return_type>::type
    { return fun(args ...); }
     
    template<size_t NbRemaining, typename Fun, typename Array, typename ... Args>
    inline auto private_call(Fun const& fun, Array const& arr, Args ... args) ->
    typename std::enable_if<0u < NbRemaining, typename function_traits<Fun>::return_type>::type
    {
      return private_call<NbRemaining - 1u, Fun, Array, decltype(std::get<NbRemaining-1>(arr)), Args...>
        (fun, arr, std::get<NbRemaining-1>(arr), args ...);
    }
     
    template<typename Fun, typename Array>
    inline auto call(Fun const& fun, Array const& arr) ->
    typename function_traits<Fun>::return_type
    {
      return private_call<size_traits<Array>::size, Fun, Array>(fun, arr);
    }
     
    // Adapt based on Flob90
    template<class Seq, class Fun>
    std::function<void(const Seq&)>
    make_adapt(Fun&& fun)
    {
      auto functor = make_function(fun);
      return
        [functor](const Seq& seq) {
          static_assert(size_traits<Seq>::size == function_traits<decltype(functor)>::arity, "whatever");
          call(functor, seq);
        };
    }
     
    template<class Seq, class Fun>
    std::function<void(const Seq&)>
    make_adapt_dyn(Fun&& fun)
    {
      auto functor = make_function(fun);
      return
        [functor](const Seq& seq) {
          if (function_traits<Fun>::arity != seq.size()) throw "whatever";
          std::array<typename Seq::value_type, function_traits<decltype(functor)>::arity> arguments;
          std::copy(std::begin(seq), std::end(seq), std::begin(arguments));
          call(functor, arguments);
        };
    }
     
    void foo(int a, int b){ std::cout << (a+b) << std::endl; }
     
    int main()
    {
      std::array<int, 2> params1 {1, 2};
      auto apply_fun1 = make_adapt<decltype(params1)>(foo);
      apply_fun1(params1);
     
      std::tuple<int, int> params2 {3, -5};
      auto apply_fun2 = make_adapt<decltype(params2)>(foo);
      apply_fun2(params2);
     
      std::vector<int> params3 {8, 7};
      auto apply_fun3 = make_adapt_dyn<decltype(params3)>(foo);
      apply_fun3(params3);
    }
    Edit: fix pour lambda
    Find me on github

  3. #23
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @jblecanard: Bien entendu qu'on peut, mais je n'envisage jamais de me passer des bibliothèques header-only de boost (du coup j'ai même pas essayé).

    Pour le moment je n'ai regardé que ton main (et corrigé quelques warning qui sont en erreur chez moi), si j'ai bien compris le cas d'utilisation de white_tentacle, seul le troisième cas de ton main correspond à son cas (j'ai viré les deux premiers tests et make_adapt pour ne garder que make_adapt_dyn donc), et idéalement il doit fonctionner pour toute sorte de foncteurs (monomorphiques), c'est pas le cas de ton code. Il ne fonctionne pas pour une lambda, mais ceci se corrige en remplacement Fun par declatype(functor) lors de l'utilisation de ton trait, et il ne fonctionne pas pour un foncteur du type : struct A { void operator()(int,int){} };.

    J'ai pas encore regardé en détail ton code, mais je pense avoir d'autres remarque d'ordre plus générales dessus (que je ferais après avoir regardé plus en détails).

  4. #24
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut

    Des warnings, curieux, je n'en ai aucun (clang 3.4 et gcc 4.9), je suis curieux ? D'après ce que j'ai compris de son dernier post, white_tentacle bosse plutôt sur des tuples que des structures dynamiques, à lui de nous dire. Après ben je pense qu'on a pas mal fait le tour du sujet .
    Find me on github

  5. #25
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Alors quelques remarques plus générales (et personnelles) :
    1. Je suis assez critique vers l'utilisation de structure regroupant plusieurs traits, cella limite l'utilisation des traits en méta-programmation. Avec un seul trait par structure et le choix d'une convention de notation, les traits deviennent des méta-fonctions.
    2. Sur la partie arité du trait, la problématique est qu'il est très limité, c'est ça qui fait que ça ne marche pas avec une lambda dans ton code d'origine. Je préfère l'approche que j'ai choisis avec boost.mpl, sfinae et decltype/declval qui me permet de tester la validité d'un appel jusqu'à ce que la taille du paquet donne un appel valide.
    3. Je suis vraiment pas convaincu d'un code qui va convertir des types lorsque ce n'est pas nécessaire, ce qui est le cas avec ton make_function. On possède le type exacte au début, cet objet n'est pas stocké dans un conteneur homogène, pourquoi donc le convertir ? Note cependant qu'il existe une autre conversion (que ce soit dans mon code ou le tien) qui se produit lors de l'utilisation de std::copy/fusion::fold, idéalement c'est une vue qu'il faudrait produire.


    Edit: Pour les warning, le premier concerne un paramètre que tu nommes et que tu n'utilises pas, le second concernait une partie du code que j'ai enlevé (sur le std::array dans le main). Compilateur clang 3.4.2 en "-Werror -Weverything -Wno-c++98-compat -Wno-exit-time-destructors -Wno-global-constructors -Wno-gnu-zero-variadic-macro-arguments -Wno-disabled-macro-expansion -Wno-documentation-unknown-command -Wno-documentation -Wno-missing-prototypes".

    Edit2: La map dans son pseudo-code me fait plutôt pensé à une structure dynamique, mais en effet à lui de nous dire.

    Edit3: J'ai modifié mon code en piochant une idée dans le tien (copy à la place de fold).

  6. #26
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Tu as enlevé du code sur std::array dans le main ? Pourtant il n'y quasi rien dedans. Pour que ça marche avec des lambdas il suffit de remplace Fun par decltype(functor) dans les make_adapt.

    Sinon je suis d'accord avec toutes tes remaques, qui sont à mettre en regard du temps court que j'ai passé dessus, et du contexte : j'aime bien écrire des trucs STL pur sur une discussion de forum pour que ce soit le plus simple à comprendre possible et le plus générique possible en terme d'environnement de travail, car malheureusement, beaucoup de dev n'ont pas le droit ou la possibilité technique d'utiliser boost. Après ben si je l'ai sur un projet, t'inquiètes je ne m'en prive pas non plus
    Find me on github

  7. #27
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    En passant par des std::integer_sequence (C++14) il y a moyen de se passer de std::copy.

    Pour la compatibilité c++11/c++14:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #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>;
     
      template<class T>
      using remove_reference_t = typename remove_reference<T>::type;
     
      template<class T>
      using add_pointer_t = typename add_pointer<T>::type;
     
      template<bool C, class T, class U>
      using conditional_t = typename conditional<C, T, U>::type;
    }
    #endif
    arity_traits:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    template<class T>
    struct arity_traits
    : arity_traits<decltype(&T::operator())>
    {};
     
    template<class R, class... Args>
    struct arity_traits<R(*)(Args...)>
    : std::integral_constant<std::size_t, sizeof...(Args)>
    {};
     
    template<class R, class C, class... Args>
    struct arity_traits<R (C::*)(Args...) const>
    : std::integral_constant<std::size_t, sizeof...(Args)>
    {};
    Je n'ai pas confiance dans l'ajout d'argument jusqu'à donné une valeur valide, cela ignore les paramètres par défauts.

    Les fonctions call et make_adapt_dyn remaniées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    #include <functional>
     
    template<typename Fun, typename Seq, std::size_t... Ns>
    auto call(Fun const& fun, Seq const& seq, std::index_sequence<Ns...>)
    -> decltype(fun(seq[Ns]...))
    {
      return fun(seq[Ns]...);
    }
     
    template<class Seq, class Fun>
    std::function<void(const Seq&)>
    make_adapt_dyn(Fun&& fun)
    {
      using remove_ref = std::remove_reference_t<Fun>;
      using functor_type = std::conditional_t<
        (std::is_function<remove_ref>()),
        std::add_pointer_t<remove_ref>,
        remove_ref
      >;
    #if __cplusplus == 201103L
      return std::bind([](functor_type const & functor, const Seq& seq) {
    #else
      return [functor = fun](const Seq& seq) {
    #endif
        if (arity_traits<functor_type>() != seq.size()) throw "whatever";
        call(functor, seq, std::make_index_sequence<(arity_traits<functor_type>())>());
    #if __cplusplus == 201103L
      }, functor_type(std::move(fun)), std::placeholders::_1);
    #else
      };
    #endif
    }
    En c++11 je suis passé par std::bind. Je pense naïvement qu'il y a une copie en moins par rapport à la fonction originale de jblecanard (je n'est pas vérifié).
    Par contre, en c++14 l'usage de bind par rapport à la lambda présente ici prend plus de place en mémoire (g++-4.9). Alors j'ai mit des macros conditionnels '^^.

    Le test avec pointeur sur fonction, lambda et classe fonction.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    #include <vector>
    #include <iostream>
     
    void foo(int a, int b) { std::cout << (a+b) << std::endl; }
     
    template<class T>
    void f(T) {}
     
    int main()
    {
      {
        std::vector<int> params3 {8, 7};
        auto apply_fun3 = make_adapt_dyn<decltype(params3)>([](int a, int b) { foo(a, b);});
        apply_fun3(params3);
      }
      {
        std::vector<int> params3 {8, 7};
        auto apply_fun3 = make_adapt_dyn<decltype(params3)>(foo);
        apply_fun3(params3);
      }
      {
        std::vector<int> params3 {8, 7};
        struct F {
          void operator()(int a, int b) const
          { return foo(a, b); }
        };
        auto apply_fun3 = make_adapt_dyn<decltype(params3)>(F());
        apply_fun3(params3);
      }
    }
    @Flob90: ton code ne compile pas avec g++. Celui-ci râle quand des référence de pointeurs de fonction sont pris dans les lambdas (bug de gcc ?). Je l'ai contourné en enlevant la référence (functor_type dans make_adapt_dyn) ou une capture avec initialiseur ([functor = func](...){...}).

  8. #28
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Nickel la version avec les integer_sequence.

    Dans ma version, on peut économiser des copies en enlevant la référence comme tu l'as fait, j'ai pas poussé le travail jusque là. Quant à bind versus lambda, j'aurais tendance à plus me concentrer sur le surcoût d'appel que sur l'espace occupé en mémoire par le foncteur pour les comparer. Je m'embêterais pas avec une macro conditionnelle là dessus à moins d'un goulet mis en évidence par un test de perfo, mais bravo pour l'effort .
    Find me on github

  9. #29
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Bon, j’ai réussi à faire marcher ça à peu près correctement.

    Les std::bind ne fonctionnent pas, mais les lambdas fonctionnent (et vu que les lambdas ont de bien meilleures perf que bind, au moins avec mon compilo actuel, ce n’est pas un soucis).

    Globalement, j’ai repris la solution de jblecanard en l’adaptant un petit peu par rapport à mes types réels.

    Il y a aussi un autre truc qui ne fonctionne pas, c’est l’initialisation de mes paramètres depuis une brace-initializer-list. Là, je pense qu’il y a peu d’espoir mais je peux faire avec.

    Merci à tous pour vos suggestions

    J’essaierai de poster une version épurée de la chose.

  10. #30
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @jo_link_noir:Avec le trait comme je l'ai fait, foo(int=0) va donné une arité de 1 (*). En faite l'intérêt principal que je vois dans la façon que j'ai faite ici, c'est qu'on oubli pas de cas comme ça (par exemple tu as oublié le cas R(Arg...), qui apparaît naturellement si je veux l'arité de foo avec arity_traits<decltype(foo)>::value, à moins que tu l'ai écarté volontairement ?).

    Pour gcc, en effet j'avais pas testé avec lui, je ne sais pas si il a raison ou tort par contre.

    J'aime bien l'utilisation de std::index_ par contre, je vais la mettre en place dans ce que j'avais fait (message où j'ai mis mon code édité avec ton idée, pas corrigé le bug gcc).

    (*) Quelque soit l'implémentation du trait qu'on ai fait dans ce sujet, c'est le type que l'on traite, hors les paramètres par défaut ne rentrent pas en compte dedans. On doit pouvoir faire un truc qui avec une syntaxe du genre decltype(arity(foo)); donnerait une séquence mpl contenant l'ensemble des "arités" possibles, mais je pense que l'intérêt est faible et la syntaxe tortueuse.

  11. #31
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    @Flob90:
    (J'avais testé l'arité avec foo(int, int=0) qui donne aussi 1.)

    J'ai mit volontairement de côté les références qualifiers et les opérateurs non constant, le reste est un oubli. Il faut dire que tous mettre devient rapidement verbeux et sur ce point ta méthode est beaucoup plus simple.
    Je me suis rendu compte avant d'aller me coucher hier qu'elle possède également un autre avantage: elle permet -dans certaines conditions- de fonctionner sur les foncteurs et lambdas polymorphiques. Chose impossible avec mon trait.
    Au final j'en viens à préférer ton trait ^^

    @jblecanard: À la base je ne testé pas l'espace occupé, mais comme je lance régulièrement met programme avec valgrind je m'en suis aperçu quand j'ai adapté pour le c++11 (je l'avais écrit en c++14). Au final j'avais les 2 versions alors j'ai mis des macros.

  12. #32
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @jo_link_noir: Ça donne 2 chez moi avec cette fonction. Et en effet ça marche pour les foncteurs polymorphiques pour peu qu'il ne fasse pas une déduction des paramètres (en gros des T tout simple ça marche), j'avais pas fait attention avant que tu le dises. J'ai actualisé mon code, avec différents tests (fonctions, fonctions template, foncteur, foncteur polymorphique, lambda et bind) ils passent sous clang (pas sous gcc par contre).

  13. #33
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    @Flob90: Ah oui, effectivement. Allez dernier essai (la fatigue tous ça ). Si le paramètre par défaut et dans un operator() il est ignoré.
    Pour gcc, en c++14 mettre [fun=fun] dans la capture de la lambda suffit... Il n'y pourtant pas de différence avec [fun], si ?

    PS: pas de 'e' à jo

  14. #34
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @jo_link_noir: (désolé pour le e) En effet dans un foncteur il le prend pas (et c'est logique), mais je vais remédier à ça d'ici quelques minutes/heures (j'avais commencé à le faire, l'idée est de lister l'ensemble des appels valides en testant de 0 à N, disons N=9, ca fait déjà un bon paquet de paramètre) et ensuite de parcourir l'ensemble de ces quantités valide jusqu'à matcher la longueur de la séquence. Pour le truc des lambda, j'en sais absolument rien, mais on va faire comme ça en attendant.

    J'ai modifié mon code (toujours dans le même message), ça fonctionne bien, et ça prend en compte les foncteurs avec paramètres par défaut, avec plusieurs surcharges et avec des templates (dans une certaine mesure). Notons par contre que ca ne marche pas réellement pour les expressions issues de bind car celle-ci peut supporter (d'un point de vue validité des appels) autant de paramètres que l'on veut.

  15. #35
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Bon, voici une version (très) simplifiée de ce que j’utilise au final :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
     
    #include <array>
    #include <iostream>
    #include <tuple>
    #include <functional>
    #include <map>
     
    struct myvariant
    {
        int type;
        union value {
            int ival;
            double dval;
        };
        value val;
        myvariant() { type = 0; val.ival = 0; }
        myvariant(int a) {
            type = 0;
            val.ival = a;
        }
        myvariant(double d) { type = 1; val.dval = d; }
    };
     
    template <typename ReturnType, typename... Args> struct base_traits {
      using return_type = ReturnType;
      using function = const std::function<ReturnType(Args...)>;
      static size_t constexpr arity = sizeof ... (Args);
    };
    template <typename Function> struct function_traits;
    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(Args...)> : public base_traits<ReturnType, Args...> {};
    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(&)(Args...)> : public base_traits<ReturnType, Args...> {};
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const> : public base_traits<ReturnType, Args...> {};
    template <typename ReturnType, typename... Args>
    struct function_traits<std::function<ReturnType(Args...)>> : public base_traits<ReturnType, Args...> {};
     
    template<typename T>
    auto make_function(T const& f) ->
    typename std::enable_if<std::is_function<T>::value && !std::is_bind_expression<T>::value, std::function<T>>::type
    { return f; }
     
    template<typename T>
    auto make_function(T const& f) ->
    typename std::enable_if<
        !std::is_function<T>::value && !std::is_bind_expression<T>::value,
        typename function_traits<decltype(&T::operator())>::function>::type
    { return static_cast<typename function_traits<decltype(&T::operator())>::function>(f); }
     
    template<typename T>
    struct variant_value {
     
    };
     
    template<>
    struct variant_value<myvariant const&> {
        static myvariant const& value(myvariant const& item) { return item; }
    };
     
    template<>
    struct variant_value<int> {
        static int value(myvariant const& item) { return item.val.ival; }
    };
     
    template<>
    struct variant_value<double> {
        static double value(myvariant const& item) { return item.val.dval; }
    };
    std::pair<bool, std::map<std::string,myvariant>::const_iterator> check_arg(std::string name, int typeIndex, std::map<std::string,myvariant> const& parameters)
    {
        auto it = parameters.find(name);
        if(it != parameters.end())
        {
            if(it->second.type == typeIndex)
                return std::make_pair(true, it);
            else
                return std::make_pair(false, parameters.end());
        }
        else
            return std::make_pair(false, it);
    }
     
    template<typename T> 
    struct type_index
    {
    };
     
    template<>
    struct type_index<int>
    {
        static constexpr const int value = 0;
    };
     
    template<>
    struct type_index<double>
    {
        static constexpr const int value = 1;
    };
     
    template<const int n, typename... FuncArgs>
    struct function_invoker
    {
        template<typename Array, typename Fun, typename... Args>
        static auto call(Fun fun, Array const& arr, std::map<std::string,myvariant> const& parameters, Args... arg) ->
             decltype(function_invoker<n-1, FuncArgs...>::call(fun, arr, parameters, variant_value< typename std::tuple_element<n-1, std::tuple<FuncArgs...> >::type >::value(myvariant(0)), arg...))
        {
            typedef typename std::tuple_element<n-1, std::tuple<FuncArgs...> >::type itemType;
            auto result = check_arg(std::get<n-1>(arr), type_index<itemType>::value, parameters);
            if(!result.first)
            {
                std::cerr << "arg error" << std::endl;
                return -1; // -1 is error;
            }
            else
                return function_invoker<n-1, FuncArgs...>::call(fun, arr, parameters, variant_value<itemType>::value(result.second->second), arg...);
        }
    };
     
    template<typename... FuncArgs>
    struct function_invoker<0, FuncArgs...>
    {
        template<typename Array, typename Fun, typename... Args>
        static auto call(Fun fun, Array /*arr*/, std::map<std::string,myvariant> const& /*parameters*/, Args... arg) -> decltype(fun(arg...))
        {
            return fun(arg...);
        }
    };
    template<typename Array, typename... Args>
    std::function<int(std::map<std::string,myvariant> const&)> createFunction2(Array const& paramDescription, std::function<int(Args...)> func)
    {
        return [func, paramDescription]
                (std::map<std::string,myvariant> const& parameters)
        {
            return function_invoker<sizeof...(Args), Args...>::call(func, paramDescription, parameters);
        };
    }
     
    template<typename Array, typename Fun>
    std::function<int(std::map<std::string, myvariant>)> createFunction(Array const& paramDescription, Fun fun)
    {
        auto f = make_function(fun);
        return createFunction2(paramDescription, f);
    }
     
     
     
    int sum1(int a, int b, int c, int d, int e, int f, double dv)
    {
        std::cerr << a << " " << b << " " << c << " " << d << " " << e << " " << f << " " << dv << std::endl;
        return a + b + c + d + e + f;
    }
     
    int main()
    {
        std::array<std::string, 7> desc = {{"first","second","third","fourth", "fifth","sixth", "double"}};
        std::map<std::string, myvariant> args;
    //    auto fun = createFunction(desc, sum1); not working, strange...
        auto f2 = make_function(sum1);
        auto fun = createFunction(desc, f2);
        std::cerr << fun(args) << std::endl;
        args = {{"first", 1}, {"second", 2}, {"third", 3}, {"fifth", 5}, {"fourth", 4},{"sixth", 6},{"double", 4.3}};
        std::cerr << fun(args) << std::endl;
        args["first"].type = 1;
        args["first"].val.dval = 2.3;
        std::cerr << fun(args) << std::endl;
        return 0;
    }
    qui renvoie bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    arg error
    -1
    1 2 3 4 5 6 4.3
    21
    arg error
    -1
    J’ai quand même un soucis sur cet exemple (que je n’ai pas dans mon vrai code, j’ai probablement pété un truc en simplifiant) sur le make_function : make_function(sum1) fonctionne bien mais pas dans createFunction, là je sèche pour l’explication.

    Note : au départ, j’accumulais les valeurs dans un tuple pour ensuite appeler avec call, mais c’est incompatible avec les types références : du coup, j’ai modifié un peu la chose pour se passer complètement du tuple et récupérer les paramètres en même temps qu’on construit l’appel.

  16. #36
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Ha tu es donc resté sur ma première proposition. J'en suis venu à préférer celle de Flob90 qui s'est bien raffinée après les différents ajouts. Le moins que l'on puisse dire, c'est que ce fut un post très collaboratif !
    Find me on github

  17. #37
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Ha tu es donc resté sur ma première proposition. J'en suis venu à préférer celle de Flob90 qui s'est bien raffinée après les différents ajouts. Le moins que l'on puisse dire, c'est que ce fut un post très collaboratif !
    Je reste sur du code que je comprends . J’ai besoin de relire ce qu’a fait Flob90 à tête reposée pour comprendre comment tout ça fonctionne.

    En tout cas, merci à tous, oui !

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Réponses: 4
    Dernier message: 30/05/2011, 19h38
  2. Réponses: 2
    Dernier message: 10/01/2009, 13h38
  3. Problème de class template et std::map
    Par bathof dans le forum Langage
    Réponses: 2
    Dernier message: 31/07/2007, 22h18
  4. template et std::vector
    Par themadmax dans le forum Langage
    Réponses: 9
    Dernier message: 26/07/2006, 10h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo