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

Langage C++ Discussion :

Quelques questions à propos du langage lui même.


Sujet :

Langage C++

  1. #1
    Invité
    Invité(e)
    Par défaut Quelques questions à propos du langage lui même.
    Bonjour!

    J'ai quelque questions à propos du langages pour savoir si il y a moyen de faire les choses suivantes ou pas.

    -Y a t'il moyen d'allouer un pointeur de fonction sur le tas ? Faire quelque chose comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void(*func)(int, int, etc...) = new (int args1, int args2, etc...) { //Definition de la fonction ici. }
    Je sais qu'il existe les fonctions anonyme mais celles-ci ont un défaut : le compilateur ne peux pas déduire leur type.
    Ce qui alourdi la syntaxe dans le cas de l'utilisation d'un wrapper sur la fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    wrapper.setParams<std::function<void(int arg1, int arg2, etc...)>> (val1, val2, etc...);
    Hors que je n'ai pas besoin de passer de paramètre template pour les fonctions membres et les fonctions non membre, car, le compilateur peut déduire automatiquement le type de la fonction dans ce cas la.

    L'avantage du wrapper est que je peux stocker le pointeur de fonction quelque part (dans un tableau par exemple), et changer les paramètres de la fonction n'importe ou dans le programme comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    wrapper.setParams<std::function<void(int arg1, int arg2, etc...)>, 1, 2> (val1, val2);
    Ce qui n'est pas possible avec std::bind et les placeholders.

    Seconde question : est-il possible de récupérer la signature d'une fonction membre d'une classe ? (Via le binaire de la classe par exemple)

    Ce que je voudrais c'est enregistrer les signatures des fonctions membre d'une classe dans une autre classe automatiquement à la compilation, afin de les appeler à l'exécution, faire un genre de système de réflexion en gros, ceci dans le but de pouvoir appeler des fonctions template virtuelle de la sorte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    baseClass.call(Signature, object, val1, val2, etc...)
    Pour l'instant j'enregistre tout ça manuellement dans le main à l'aide de macros mais je voudrais savoir si il n'y a pas moyen de faire un système pour les enregistrer automatiquement.

    Voilà merci d'avance pour vos réponses.

    Si je demande ça c'est parce que je suis entrain de coder une bibliothèque ici :
    http://lolilolightdevblog.wordpress.com/
    Dernière modification par Invité ; 30/08/2014 à 19h44. Motif: Ajour des balises CODE

  2. #2
    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
    Bonjour,

    Comment-ça le compilateur ne peut pas déduire le type d'une lambda ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #include<iostream>
    #include<typeinfo>
     
    template<class F>
    void foo(F)
    { std::cout << typeid(F).name(); }
     
    int main()
    { foo([](){}); }
    Fonctionne très bien chez moi.

    Pour le second point, dans le binaire c'est assez peu probable. Une fois la compilation faite, il doit rester assez peu d'information sur le typage.

  3. #3
    Invité
    Invité(e)
    Par défaut
    Oui ça marche aussi chez moi si la lambda ne prend pas d'arguments en paramètre.

    Mais dès que je veux passer des arguments à la lambda, le compilateur ne peut plus me le déduire donc je dois faire ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    wrapper (std::function<void(int)>([](int) {//Définition de la fonction ici!}));

    Je dois "convertir" la lambda expression en std::function, en gros lui dire que la lamba expression est une fonction anonyme et pas une lamba4 et quelque chose (je sais pas trop quoi)

    Pour le second point cela serait plus compliqué, je ne sais pas si il y a moyen de récupérer des informations sur les fonctions membres en ayant le type de la classe avec le RTTI, ainsi, j'enregistre tout automatiquement dans la factory avec le RTTI et je peux appeler une fonction en lui passant sa signature, sa classe de base et un pointeur sur un objet.

    Je sais que en java c'est possible avec les classes Class et Method mais en c++ je ne sais pas.

  4. #4
    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
    Le code que j'ai donné marche tout aussi bien avec [](int){}.

    Tu as un exemple simple et complet (que je puisse copier/coller donc) qui me permettrait de voir le problème exacte que tu as avec les lambda ?

  5. #5
    Invité
    Invité(e)
    Par défaut
    Ha non j'ai rien dis ça compile, par contre pour changer la valeur de l'argument de l'expression, je suis obligé de faire ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     odfaeg::FastDelegate<void> delegate([](int arg) {std::cout<<"arg : "<<arg;}, 1);
     delegate.setFuncParams<std::function<void(int)>>(2);
     delegate();

    Sinon, il ne me le déduit pas ici :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template <typename F, typename... A> void setFuncParams(A... args) {
            if (static_cast<FastDelegate6<F, A...>*>(delegate)) {
                static_cast<FastDelegate6<F, A...>*>(delegate)->setParams(args...);
            }
        }

    Je pense que il n'y a pas moyen d'enregistrer des expressions lambda qui capture des valeurs, cependant ici je peux changer la valeur des paramètres de la lambda expression ou je le veux dans le code car j'ai fais un système de "type erasure" ce que std::bind ne fais pas.
    Et j'ai utilisé un tuple pour stocker la valeur des arguments de la fonction et les types des arguments.

    Sinon il me semble que c++ ne garde aucune trace sur les définitions des classes dans le binaire comme le fait java. :/

  6. #6
    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
    F ne sera jamais déduit dans cette fonction template car F n'est pas utilisé en paramètre de la fonction. A-tu un exemple d'utilisation de cette fonction avec une lambda qui pose problème ? Tu n'utiliserais pas un decltype sur une expression lambda, du genre decltype([](){}) ? Parce que ça c'est en effet impossible.

  7. #7
    Invité
    Invité(e)
    Par défaut
    Ha oui un decltype pourquoi pas. ^^

    En effet je ne passe pas la lambda expression en paramètre à la fonction, cependant, ça marche pour les fonctions normales (Là il peut me déduire le type du pointeur de fonction même si je ne passe pas le pointeur de fonction en argument.)

    Je pense que je vais utiliser un decltype.

    Donc c'est impossible de créer bêtement une fonction anonyme en allouant bêtement un pointeur de fonction sur le tas, il faut d'office passé par une lambda expression.

  8. #8
    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
    Non, il ne te déduit rien avec cette fonction, que ce soit un pointeur de fonction, un foncteur plus générale ou une lambda. Je ne sais pas ce que tu veux dire exactement, mais en tout cas ce n'est pas de la déduction de paramètre template. Tu peux montrer comment tu utilises ta fonction avec un fonction et avec une lambda, que je comprennes ce que tu fais exactement ?

    On ne peut pas faire de decltype sur une expression lambda.

    Tu peux stocker tout les foncteur que tu veux avec std::function.

  9. #9
    Invité
    Invité(e)
    Par défaut
    Ha bon pourtant avec les fonctions normales j'y arrive :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    odfaeg::FastDelegate<void> delegate (&MyAppli::leftMouseButtonPressed, this, sf::Vector2f(-1, -1));
    delegate.setParams(this, sf::Vector2f(0, 0));

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     template <typename O, typename ...A> void setParams(O* object, A... args) {
            if (static_cast<FastDelegate2<O, A...>*>(delegate)) {
                static_cast<FastDelegate2<O, A...>*>(delegate)->setParams(object, args...);
            }
        }

    Là il me déduit automatiquement que c'est un pointeur sur fonction membre de la classe MyAppli.

    Ce qu'il ne fait pas pour les fonctions anonyme.

  10. #10
    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
    La déduction c'est sur setFuncParams ou setParams ? Parce que tu montres le code de l'une puis tu utilises l'autre.

    Je commence à voir ce que tu fais, tu stocks comment le fonction dans ton objet FastDelegate ? C'est du type-erasure je suppose, et tu l'as refais à la main à la place d'utiliser directement std::function ? Du coup tu peux stocker ce que tu veux, sauf que rien dans un foncteur ne donne d'information sur la signature, excepté pour les pointeurs/références sur fonction (et std::function), du coup en effet si à un moment tu en as besoin tu vas devoir lui indiquer. Tu as tester ton code avec un foncteur fait main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    struct Fun
    { void operator()(int) const {} };
    Je pense que ton code va bloquer aussi.

    Dans tout les cas, tu ne pourras jamais déduire exactement la signature, typiquement dès que ton foncteur est polymorphique, tu fais quoi ? La signature n'est pas unique (et dans certain cas indénombrable).

    PS: Ton code est sur un dépôt, que je vois l'ensemble ?

  11. #11
    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
    J'ai récupéré le code sur ton dépôt Git. Pour la discussion, j'ai restreint le code à une partie, celle-ci :
    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
     
    namespace odfaeg {
     
    struct Delegate
    { virtual void operator()() = 0; };
     
    template<class F, class... A>
    struct FastDelegate6 : Delegate {
    	FastDelegate6(F func, A... args) {
                typedef void(*CB)(F,A...);
                CB cb = &callback;
                funct = new Functor<void(F,A...)>(cb, func);
                params = std::make_tuple(args...);
            }
    	void setParams (A... args)
    	{ params = std::make_tuple(args...); }
    	void operator()()
    	{ (*funct)(params); }
     
    private :
    	std::tuple<A...> params;
    	Functor<void(F, A...)> *funct;
    };
     
    template<class... A> 
    struct FastDelegate0 : Delegate {
    	FastDelegate0 (void(*f)(A...), A... args) {
                typedef void(*CB)(Function<void(A...)>,A... args);
                CB cb = &callback;
                funct = new Functor<void(Function<void(A...)>,A...)>(cb,Function<void(A...)>(f));
                params = std::make_tuple(args...);
            }
    	void setParams (A... args)
    		{ params = std::make_tuple(args...); }
    	void operator()()
    		{ (*funct)(params); }
     
    private :
    	std::tuple<A...> params;
    	Functor<void(Function<void(A...)>,A...)> *funct;
    };
     
    struct FastDelegate {
        template<class F, class... A>
    	FastDelegate(F f, A... args)
    	{ delegate = new FastDelegate6<F,A...>(f,args...); }
        template <class... A>
    	FastDelegate(void(*f)(A...), A... args)
    	{ delegate = new FastDelegate0<A...>(f,args...); }
        void operator()()
    	{ (*delegate)(); }
        template<class F, class... A>
    	void setFuncParams(A... args) {
            if (static_cast<FastDelegate6<F,A...>*>(delegate))
                static_cast<FastDelegate6<F,A...>*>(delegate)->setParams(args...);
        }
        template<class... A>
    	void setParams(A... args) {
            if (static_cast<FastDelegate0<A...>*>(delegate))
                static_cast<FastDelegate0<A...>*>(delegate)->setParams(args...);
        }
     
    private :
        Delegate* delegate;
    };
     
    }
    J'ai uniquement réduit le code et fait des changements de styles, aucun changement sur le fond.

    Je ne comprends pas ce que tu fais avec ton Functor, vu l'utilisation un std::function doit convenir. Et c'est là qu'on arrive sur quelque chose d'étrange, ta hiérarchie FastDelegate est celle d'un type-erasure, mais tu utilises en interne de celle-ci un autre type-erasure à qui tu redonnes le boulot, il y a un type-erasure de trop.

    Si ton objectif était de faire un type-erasure, il ne faut pas masquer le foncteur que tu prends derrière un std::function (ou ta classe Functor c'est pareil), mais conserver le type de base, ie utiliser les typedef de tes constructeurs comme types du sous-objet func.

    Cependant on en vient au point intéressant, ta classe fait trois chose : elle type-erasure les foncteurs, leur associe un ensemble de paramètres et passe un tuple en appel d'un foncteur. Or le premier point c'est exactement ce que fait std::function, pourquoi ne pas lui refiler le boulot et te concentrer sur les deux autres tâches (la dernière était faite par ta classe Functor mais peut être faite en interne de la classe ou par une fonction extérieur utilitaire) ? Le A... apparaît donc directement dans ta classe (pour le tuple et le type du foncteur), mais ce n'est pas un problème si tu fournis une fonction de création externe :
    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
     
    namespace odfaeg {
     
    template<class... ArgT>
    struct FastDelegate {
    	template<class F, class... ArgU>
    	FastDelegate(F&& f, ArgU&&... arg)
    		: func(std::forward<F>(f))
    		, param(std::forward<ArgU>(arg)...)
    	{}
    	void operator()() const
    	{ call(std::make_index_sequence<sizeof...(ArgT)>()); }
    	template<class... ArgU>
    	void setParam(ArgU&&... arg)
    	{ param=std::make_tuple(std::forward<ArgU>(arg)...); }
     
    private:
    	template<std::size_t... I>
    	void call(std::index_sequence<I...>) const
    	{ func(std::get<I>(param)...); }
     
    	std::function<void(ArgT...)> func;
    	std::tuple<ArgT...> param;
    };
     
    template<class F, class... Arg>
    auto make_delegate(F&& f, Arg&&... arg)
    { return FastDelegate<Arg...>(std::forward<F>(f),std::forward<Arg>(arg)...); }
     
    }
    Qui s'utilise ainsi :
    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
     
    void foo(int i, int j)
    { std::cout << i << j; }
     
    struct A {
    	void bar(int i)
    	{ std::cout << i; }
    };
     
    int main(){
    	auto f1 = odfaeg::make_delegate(foo,1,2);
    	f1();
    	f1.setParam(2,3);
    	f1();
    	std::cout << std::endl;
     
    	auto f2 = odfaeg::make_delegate(
    		[](int i, int j){ std::cout << i << j; },
    		1,2
    	);
    	f2();
    	f2.setParam(2,3);
    	f2();
    	std::cout << std::endl;
     
    	int i = 1;
    	auto f3 = odfaeg::make_delegate(
    		[i](int j){ std::cout << i << j; },
    		2
    	);
    	f3();
    	f3.setParam(4);
    	f3();
    	std::cout << std::endl;
     
    	A a;
    	auto f4 = odfaeg::make_delegate(&A::bar,&a,2);
    	f4();
    	f4.setParam(&a,3);
    	f4();
    }
    Là je traite la totalité des foncteurs possibles (j'ai pas tout testé et n'exclut pas d'avoir oublié des cas), sans refaire le type-erasure à la main : je profite de ce qui existe déjà et assemble le tout. Pour prendre en compte un type de retour, je pense qu'on peut s'en sortir sans faire une spécialisation de la classe, juste avec du SFINAE (à base d'enable_if et de decltype). Il y a un problème qui va apparaître (et que je n'ai pas traité) lors de l'utilisation de passage par référence, il faut mettre en place un mécanisme proche (identique ?) de ce que fait std::ref.

    Edit: J'ai oublié le fait que tu type-erasure aussi sur les paramètres, ce qui demande quelques modifications que j'ajouterais à la fin de ce message une fois faites.

  12. #12
    Invité
    Invité(e)
    Par défaut
    Ok, merci, je vais essayer d'améliorer ça en utilisant directement une std::function. (Et donc, sans passer par la classe Functor)

    Après réflexion c'est vrai que j'aurai pu m'en passer.

    Pour le type erasure sur le type erasure, j'en ai besoin pour stocker mes delegates dans une std::map et rechercher ainsi quel événements ont été déclenché pour appelé le bon foncteur (je fais ceci dans le fichier listener.h), je ne peux donc pas utiliser auto, ni avoir une classe qui prend des paramètres template différent, c'est pour cela que je veux que tout mes delegate soient de type FastDelegate<void> ou bien FastDelegate tout cours, ensuite je change les paramètres (en faisant un dynamic cast pour récupérer le bon type de delegate dans la classe FastDelegate ce qui explique le type erasure sur le type erasure que je fais) et j'appel le delegate avec un bête opérateur().

    Pour les référence je ne sais pas comment ça fonctionne pour les passer dans un tuple il faudrait que je me renseigne sur std::ref.

    Au final je voudrais quelque chose comme ceci, similaire à std::bind mais avec du type erasure :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    delegate.setParams<1, 2>(val1, val2);

    PS : je ne connais pas std::make_index_sequence, c'est du c++14 ?

  13. #13
    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
    Oui j'avais oublié que tu faisais ton type-erasure aussi sur les paramètres, cf mon edit (dont je mets le code ici finalement) :
    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
     
    namespace odfaeg {
     
    struct Delegate {
    	virtual void operator()() const = 0;
    	virtual ~Delegate();
    };
     
    Delegate::~Delegate(){}
     
    template<class... ArgT>
    struct FastDelegateImpl : Delegate {
    	template<class F, class... ArgU>
    	FastDelegateImpl(F&& f, ArgU&&... arg)
    		: func(std::forward<F>(f))
    		, param(std::forward<ArgU>(arg)...)
    	{}
    	void operator()() const
    	{ call(std::make_index_sequence<sizeof...(ArgT)>()); }
    	template<class... ArgU>
    	void setParam(ArgU&&... arg)
    	{ param=std::make_tuple(std::forward<ArgU>(arg)...); }
     
    private:
    	template<std::size_t... I>
    	void call(std::index_sequence<I...>) const
    	{ func(std::get<I>(param)...); }
     
    	std::function<void(ArgT...)> func;
    	std::tuple<ArgT...> param;
    };
     
    struct FastDelegate {
    	template<class F, class... Arg>
    	FastDelegate(F&& f, Arg&&... arg) :
    		delegate(std::make_unique<FastDelegateImpl<Arg...>>
    			(std::forward<F>(f),std::forward<Arg>(arg)...)
    		)
    	{}
    	void operator()() const
    	{ (*delegate)(); }
    	template<class... Arg>
    	void setParam(Arg&&... arg)
    	{
    		using DynamicType = FastDelegateImpl<Arg...>*;
     
    		if(dynamic_cast<DynamicType>(delegate.get()))
    			dynamic_cast<DynamicType>(delegate.get())->setParam(std::forward<Arg>(arg)...);
    	}
     
    private:
    	std::unique_ptr<Delegate> delegate;
    };
     
    }
    Qui s'utilise ainsi :
    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
     
    void foo(int i, int j)
    { std::cout << i << j; }
     
    struct A {
    	void bar(int i)
    	{ std::cout << i; }
    };
     
    int main(){
    	odfaeg::FastDelegate f1(foo,1,2);
    	f1();
    	f1.setParam(2,3);
    	f1();
    	std::cout << std::endl;
     
    	odfaeg::FastDelegate f2(
    		[](int i, int j){ std::cout << i << j; },
    		1,2
    	);
    	f2();
    	f2.setParam(2,3);
    	f2();
    	std::cout << std::endl;
     
    	int i = 1;
    	odfaeg::FastDelegate f3(
    		[i](int j){ std::cout << i << j; },
    		2
    	);
    	f3();
    	f3.setParam(4);
    	f3();
    	std::cout << std::endl;
     
    	A a;
    	odfaeg::FastDelegate f4(&A::bar,&a,2);
    	f4();
    	f4.setParam(&a,3);
    	f4();
    }
    PS: Oui C++14 make_index_sequence.

    Edit: Du coup il manque aussi l'opérateur d'affectation.

  14. #14
    Invité
    Invité(e)
    Par défaut
    Sinon, j'ai une question en ce qui concerne les std::function comment tu traites ce cas la ?

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template <typename O, typename D, typename ...A> void setParams(D* derived, A... args) {
            if (static_cast<O*>(derived)) {
                static_cast<FastDelegate2<O, A...>*>(delegate)->setParams(static_cast<O*>(derived), args...);
            }
        }

    Imagine que la classe O est une classe dérivée et D une classe de base, je créer donc un pointeur de fonction sur une fonction d'une classe dérivée.
    Lors de l'appel du foncteur, je lui passe un pointeur sur un type de base, je dois donc faire un cast pour récupéré le type dynamique de l'objet avant d'appelé le foncteur sinon il va me dire que la classe de base n'a pas la fonction de la classe dérivée. (ce qui est logique)

    Comment on gère ça avec std::function ? (Je suis obligé d'utiliser un foncteur perso et de connaître la classe du pointeur de fonction sinon je ne sais pas faire le cast vu que le type de la classe qui contient le pointeur de fonction est caché par std::function.

    Je peux donc pas utiliser std::function. :/

    Et personnellement je n'ai jamais été partisant d'utilisé un std::function, imagine ce que ça donnerais dans le cadre de l'héritage multiple ou encore l'héritage virtuel.

    Et je ne suis pas sûr que appeler un pointeur de fonction sur une fonction virtuelle (c'est à dire dans le sens inverse cette fois) ne me donnera pas de comportement indéterminé selon les différents type de compilateurs dans le cadre de l'héritage multiple et l'héritage virtuel.

    Et puis je suis obligé de caster l'objet dérivé en un objet de base pour appeler la fonction virtuelle de la classe de base dans l'autre cas c'est à dire si O est une classe de base et D une classe dérivée.

    PS : Masquer les types c'est bien mais ......

  15. #15
    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
    Pour les cas spécifiques tu peux toujours faire un wrapper intermédiaire ce n'est pas un gros problème. Mais les cas que tu cites sont typiquement ceux où je mets le code à la poubelle si je les vois plus d'une fois, alors de là à les intégrer de manière générique ...

    Le seul problème qu'il y a c'est le premier que tu cites, les autres ne posent pas de problèmes. (multiple, virtuel et upcast fonctionnent bien)

    Tu as tord de ne pas utiliser std::function dès que tu es dans un cas de stockage de foncteur, il est là pour ça.

  16. #16
    Invité
    Invité(e)
    Par défaut
    Ok, mais je voulais utiliser le même wrapper pour tout les cas, maintenant je sais que l'héritage virtuel je le fais jamais car c'est du code à jeter à la poubelle comme tu dis.

    Pour le downcast dans le 1er cas que je cite ça peut être très utile, dans le cadre d'une factory par exemple, si tu veux enregistrer des types polymorphique et des fonctions et appeler la bonne fonction à l'exécution sur le type dynamique de l'objet. (Je fais ça pour mon système de sérialisation)

    Ainsi je n'ai pas besoin de connaître le type dynamique de l'objet lors de l'appel du pointeur sur la fonction. (Surtout que je ne le connais pas dans le cadre de fonctions template qui ne peuvent pas être virtuelle, je dois donc savoir quel fonction je dois appeler à l'exécution, et pour cela il n'y a qu'une seul moyen : type erasure, polymorphisme statique et RTTI.)

    Je peux ainsi enregistrer mes objets dans n'importe quel type d'archive, aussi bien des archives textes ou binaire que des archives xml par exemple.

    Boost utilise aussi un système similaire il me semble.

  17. #17
    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
    Quelque chose dans ce goût là :
    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
     
    namespace odfaeg {
     
    struct Delegate {
    	Delegate() =default;
    	virtual std::unique_ptr<Delegate> clone() const = 0;
    	virtual void operator()() const = 0;
    	virtual ~Delegate();
    protected:
    	Delegate(const Delegate&){}
    	Delegate& operator=(const Delegate&)
    	{ return *this; }
    };
     
    Delegate::~Delegate(){}
     
    template<class... ArgT>
    struct FastDelegateImpl : Delegate {
    	template<class F, class... ArgU>
    	FastDelegateImpl(F&& f, ArgU&&... arg)
    		: func(std::forward<F>(f))
    		, param(std::forward<ArgU>(arg)...)
    	{}
    	std::unique_ptr<Delegate> clone() const
    	{ return std::make_unique<FastDelegateImpl>(*this); }
    	void operator()() const
    	{ call(std::make_index_sequence<sizeof...(ArgT)>()); }
    	template<class... ArgU>
    	void setParam(ArgU&&... arg)
    	{ param=std::make_tuple(std::forward<ArgU>(arg)...); }
     
    private:
    	template<std::size_t... I>
    	void call(std::index_sequence<I...>) const
    	{ func(std::get<I>(param)...); }
     
    	std::function<void(ArgT...)> func;
    	std::tuple<ArgT...> param;
    };
     
    struct FastDelegate {
    	template<class F, class... Arg>
    	FastDelegate(F&& f, Arg&&... arg) :
    		delegate(std::make_unique<FastDelegateImpl<Arg...>>
    			(std::forward<F>(f),std::forward<Arg>(arg)...)
    		)
    	{}
    	FastDelegate(FastDelegate& rhs) : delegate(rhs.delegate->clone())
    	{}
    	FastDelegate(const FastDelegate& rhs) : delegate(rhs.delegate->clone())
    	{}
    	FastDelegate(FastDelegate&& rhs) =default;
    	FastDelegate& operator=(const FastDelegate& rhs)
    	{ return operator=(FastDelegate(rhs)); }
    	FastDelegate& operator=(FastDelegate&&) =default;
    	void operator()() const
    	{ (*delegate)(); }
    	template<class... Arg>
    	void setParam(Arg&&... arg)
    	{
    		using DynamicType = FastDelegateImpl<Arg...>*;
     
    		if(dynamic_cast<DynamicType>(delegate.get()))
    			dynamic_cast<DynamicType>(delegate.get())->setParam(std::forward<Arg>(arg)...);
    	}
     
    private:
    	std::unique_ptr<Delegate> delegate;
    };
     
    template<class C, class... ArgT>
    struct DynamicWrapper {
    	DynamicWrapper(void (C::*pf)(ArgT...)) : pfunc(pf){}
    	template<class O, class... ArgU>
    	void operator()(O* o, ArgU&&... arg) const
    	{
    		if(dynamic_cast<C*>(o))
    			(dynamic_cast<C*>(o)->*pfunc)(std::forward<ArgU>(arg)...);
    	}
    private:
    	void (C::*pfunc)(ArgT...);
    };
     
    template<class C, class... Arg>
    auto make_dynamic(void (C::*pf)(Arg...))
    { return DynamicWrapper<C,Arg...>(pf); }
     
    }
    Utilisation :
    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
     
    void foo(int i, int j)
    { std::cout << i << j; }
     
    struct A {
    	void bar(int i)
    	{ std::cout << i; }
    };
     
    struct B {
    	virtual void foo()
    	{ std::cout << 1; }
    	virtual ~B();
    };
     
    B::~B(){}
     
    struct C : B { void foo(); };
     
    void C::foo(){ std::cout << 2; }
     
    int main(){
    	odfaeg::FastDelegate f1(foo,1,2);
    	f1();
    	f1.setParam(2,3);
    	f1();
    	std::cout << std::endl;
     
    	odfaeg::FastDelegate f2(
    		[](int i, int j){ std::cout << i << j; },
    		1,2
    	);
    	f2();
    	f2.setParam(2,3);
    	f2();
    	std::cout << std::endl;
     
    	int i = 1;
    	odfaeg::FastDelegate f3(
    		[i](int j){ std::cout << i << j; },
    		2
    	);
    	f3();
    	f3.setParam(3);
    	f3();
    	std::cout << std::endl;
     
    	A a;
    	odfaeg::FastDelegate f4(&A::bar,&a,2);
    	f4();
    	f4.setParam(&a,3);
    	f4();
    	std::cout << std::endl;
     
    	odfaeg::FastDelegate f5 = f1;
    	f5();
    	f5=f3;
    	f5();
    	std::cout << std::endl;
     
    	C c;
    	B* b = &c;
    	odfaeg::FastDelegate f6(odfaeg::make_dynamic(&C::foo),b);
    	f6();
    	std::cout << std::endl;
    }
    Après rien ne t’empêche d’intégrer make_dynamic à FastDelegate où de faire un couche supplémentaire réunissant les deux.

    Devoir downcaster n'est que rarement nécessaire, tu as qu'à directement enregistrer les pointeurs de fonction en indiquant la classe mère, en gros :
    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
     
    struct B {
    	virtual void foo()
    	{ std::cout << 1; }
    	virtual ~B();
    };
     
    B::~B(){}
     
    struct C : B { void foo(); };
     
    void C::foo(){ std::cout << 2; }
     
    //main
    	C c;
    	B* b = &c;
    	odfaeg::FastDelegate f6(&B::foo,b);    //OK
            odfaeg::FastDelegate f6(&B::foo,&c);  //OK
            odfaeg::FastDelegate f6(&C::foo,b);    //NOK
            odfaeg::FastDelegate f6(&C::foo,&c);  //OK
    Un seul cas sur quatre nécessite de downcaster et montre surtout que l'utilisateur à surement fait une bourde, il travail avec du polymorphisme, c'est les fonctions de la classe mère qu'il doit considérer, jamais celles des classes filles (pas dans ce cas du moins).

  18. #18
    Invité
    Invité(e)
    Par défaut
    Ha, j'ai enfin trouvé une solution pour utiliser la même fonction setParams pour les différents types de wrapper, bref, je vais essayer ça :

    Code cpp : 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
     
     template <typename F, typename ...A>
        FastDelegate(F&& f, A&&... args) {
            delegate = new FastDelegate6<A...>(std::forward<F>(f), std::forward<A>(args)...);
        }
        template <typename O, typename ...A> FastDelegate(void(O::*f)(A...), O* object, A... args) {
            delegate = new FastDelegate2<O, A...>(f, object, args...);
        }
        template <typename O, typename ...A> FastDelegate(void(O::*f)(A...) const, O* object,  A... args) {
            delegate = new FastDelegate2<O, A...>(f, object, args...);
        }
        template <typename O, typename D, typename ...A> FastDelegate(D* derived, A... args) {
            if (static_cast<O*>(derived)) {
                static_cast<FastDelegate2<O, A...>*>(delegate)->setParams(static_cast<O*>(derived), args...);
            }
        }
        template <typename ...A> FastDelegate(void(*f)(A...), A... args) {
            delegate = new FastDelegate0<A...>(f, args...);
        }
        void operator()() {
            (*delegate)();
        }   
        template <typename... A,
                  typename T,
                  class = typename std::enable_if<std::is_convertible<std::function<void(A...)>, void(*)(A...)>::value>::type>
        void setParams(A... args, T) {
            if (static_cast<FastDelegate6<A...>*>(delegate)) {
                static_cast<FastDelegate6<A...>*>(delegate)->setParams(args...);
            }
        }
        template <typename O, typename ...A> void setParams(O* object, A... args) {
            if (static_cast<FastDelegate2<O, A...>*>(delegate)) {
                static_cast<FastDelegate2<O, A...>*>(delegate)->setParams(object, args...);
            }
        }
        template <typename O, typename D, typename ...A> void setParams(D* derived, A... args) {
            if (static_cast<O*>(derived)) {
                static_cast<FastDelegate2<O, A...>*>(delegate)->setParams(static_cast<O*>(derived), args...);
            }
        }
        template<typename ...A> void setParams(A... args) {
            if (static_cast<FastDelegate0<A...>*>(delegate)) {
                static_cast<FastDelegate0<A...>*>(delegate)->setParams(args...);
            }
        }

    En me servant que du std::function pour les fonctions anonyme, je ne veux pas m'en servir pour les autres types de fonction à cause du problème qu'il pourrait y avoir avec le downcast donc je préfère utiliser mon wrapper pour les fonctions non anonyme.

    std::function est plutôt utile dans le cadre des fonctions anonyme je trouve, pour le reste, un simple wrapper tout bête comme j'ai fait suffit.

  19. #19
    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
    Je te trouve assez paradoxal : tu dis vouloir faire quelque chose de simple aussi bien en interne qu'en utilisation pour ta bibliothèque, puis tu sors un code extrémement compliqué pour prendre en compte un cas qui n'a que peu de raison d'être et où il existe un contournement relativement simple lorsqu'il apparait (et qu'on peut inclure dedans si tu le veux de manière systématique).

    std::function est bien antérieur aux lambda, donc non ils ne se limitent pas à celle-ci (et c'est déjà un simple wrapper, aucun raison valable de le refaire).

  20. #20
    Invité
    Invité(e)
    Par défaut
    Bah j'ai essayé hein mais ton code résulte en un crash dans mon cas :

    Code cpp : 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
     
    #define REGISTER_DERIVED(ID, BASE, TYPE) \
    inline BASE* create##ID() { \
        return new TYPE(); \
    }
    #define REGISTER_TYPE(ID, BASE, TYPE) \
    { \
    odfaeg::BaseFact<BASE>::register_type(typeid(TYPE).name(), &create##ID); \
    }
    #define REGISTER_FUNC(ID, funcName, FID, BASE, TYPE, args...) \
    { \
    REGISTER_TYPE(ID, BASE, TYPE) \
    odfaeg::FastDelegate<void> delegate##ID##funcName##FID (&TYPE::vt##funcName, args); \
    odfaeg::BaseFact<BASE>::register_function(typeid(TYPE).name(), #funcName, #FID, delegate##ID##funcName##FID); \
    }
    #define CALL_FUNC(BASE, funcName, funcArgs, object, args...) \
    { \
    odfaeg::BaseFact<BASE>::callFunction(typeid(*object).name(), funcName, funcArgs, object, args...); \
    }
    #define EXPORT_CLASS_GUID(ID, BASE, TYPE) \
    REGISTER_TYPE(ID, BASE, TYPE) \
    { \
    odfaeg::OTextArchive *outa##ID = nullptr; \
    odfaeg::ITextArchive *ina##ID = nullptr; \
    TYPE* derived##ID =nullptr; \
    REGISTER_FUNC(ID, serialize, OTextArchive, BASE, TYPE, derived##ID, outa##ID) \
    REGISTER_FUNC(ID, serialize, ITextArchive, BASE, TYPE, derived##ID, ina##ID) \
    }
    template <typename B>
    class BaseFact {
        public :
        static void register_type(std::string typeName, B*(*func)()) {
            typename std::map<std::string, B*(*)()>::iterator it = types.find(typeName);
            if (it == types.end()) {
                types[typeName] = func;
            }
        }
        static void register_function(std::string typeName, std::string funcName, std::string funcArgs, FastDelegate<void> delegate) {
            typename std::map<std::string, FastDelegate<void>>::iterator it = functions.find(typeName + funcName);
            if (it == functions.end())
                functions[typeName+funcName+funcArgs] = delegate;
        }
        template <typename... A>
        static void callFunction(std::string typeName, std::string funcName, std::string funcArgs, A... args) {
            typename std::map<std::string, FastDelegate<void>>::iterator it = functions.find(typeName+funcName+funcArgs);
            if (it != functions.end()) {
                it->second.setParams(args...);
                (it->second)();
            }
            //throw Erreur(100, "Unregistered function exception!", 1);
        }
        static B* create (std::string typeName) {
            typename std::map<std::string, B*(*)()>::iterator it = types.find(typeName);
            if (it != types.end()) {
                return (it->second)();
            }
            //throw Erreur(100, "Unregistered type exception!", 1);
        }
        static std::string getTypeName (B* type) {
            typename std::map<std::string, B*(*)()>::iterator it = types.find(typeid(*type).name());
            if (it != types.end())
                return it->first;
        }
        private :
        static std::map<std::string, B*(*)()> types;
        static std::map<std::string, FastDelegate<void>> functions;
    };

    Il crash lors de l'appel de la fonction template polymorphique, je pense que c'est parce que (même pour les fonctions membre) il me choisit ce prototype-ci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    template<typename F, typename...A>
    FastDelegate(F&& func, A&& args...) {
    }

    Au lieu de me choisir celui là :
    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    template<typename O, typename...A>
    FastDelegate(void(O::*func)(A...), O* object, A args...) {
    }

    Donc bon c'est bien que mon std::function fasse le wrapper mais, si je peux pas distinguer un std::function d'un bête pointeur sur fonction comment je fais ?

    Bref..., ton code crash chez moi lorsque j'essaye d'enregistrer des fonctions template sur des type dérivé et de les appeler après. (Quand je n'utilise pas std::function par contre ça ne crash pas)
    Dernière modification par Invité ; 02/09/2014 à 13h11.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 5 12345 DernièreDernière

Discussions similaires

  1. Réponses: 12
    Dernier message: 13/06/2011, 09h28
  2. Réponses: 0
    Dernier message: 21/02/2011, 15h58
  3. Réponses: 1
    Dernier message: 11/03/2010, 12h01
  4. Quelques questions à propos de DreamShield
    Par kahoerre dans le forum Dreamshield
    Réponses: 10
    Dernier message: 10/06/2009, 09h44
  5. Quelques question à propos des technologies RAID
    Par DranDane dans le forum Composants
    Réponses: 6
    Dernier message: 12/08/2008, 12h40

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