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 :

Enregistrer des paramètres pour l'appel d'une autre fonction


Sujet :

C

  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut Enregistrer des paramètres pour l'appel d'une autre fonction
    Bonjour,

    Je recherche à faire des sortes de lambda en C :

    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
    struct {
         void *slot;
         Arguments argument;
    } Lambda;
     
    void foo(int);
    void foo2(char, bool);
    void foo2toi(bool);
     
    Lambda x[3] = { makeLambda(foo, 45),
                            makeLambda(foo2, 'e', true),
                            makeLambda(foo2toi, false) };
     
    // ailleurs dans le code
     
    executer(x[0]); //appelle foo(45);
    executer(x[1]); //appelle foo2('e', true);
    executer(x[2]); //appelle foo2toi(false);
    Est-ce que vous auriez une idée de comment je pourrais faire et comment sauvegarder les arguments ?

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 677
    Points
    13 677
    Billets dans le blog
    1
    Par défaut
    Pourquoi codes-tu en C ? Entre ça et ta précédente discussion (du neuf d'ailleurs ?), tu donnes de plus en plus l'impression que le C n'est pas le langage que tu souhaites utiliser

    Ce que tu fais me fais penser à ce qui est dit là : http://msmvps.com/blogs/jon_skeet/ar...-language.aspx
    J'ai le bouquin dont il parle et c'est comme ça que j'ai trouvé ce lien. C'est bien de ne pas se limiter à ce que propose le langage mais il faut aussi être capable de ne pas aller trop loin. C'est l'objet de la première partie dans le lien.

    Pour revenir à ton problème, je pense que tu auras le même genre de problème qu'avec la discussion précédente : la généricité. Si toutes tes fonctions avaient le même prototype, il me semble voir un embryon d'idée. Mais avec des prototypes différents, je sèche un peu… Peut-être en rajoutant un paramètre "type" pour indiquer le prototype souhaité et faire un switch / case dans makeLambda(). Il y a par contre le risque d'avoir des warnings de type insolubles, sauf à caster implicitement mais il n'y a plus alors de vérification de types par le compilateur.

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Pourquoi codes-tu en C ?
    Je code en C++ en fait mais j'ai besoin d'avoir une API C

    J'ai trouvé une méthode légèrement plus simple et j'ai alors utilisé un define un peu verbeux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SIGNAL( signal, (bool a, int b), (a,b) )
    Plus tard, je pourrais ajouter un mot clé que je remplacerait par ma macro.

    Pour revenir à ton problème, je pense que tu auras le même genre de problème qu'avec la discussion précédente : la généricité. Si toutes tes fonctions avaient le même prototype, il me semble voir un embryon d'idée. Mais avec des prototypes différents, je sèche un peu… Peut-être en rajoutant un paramètre "type" pour indiquer le prototype souhaité et faire un switch / case dans makeLambda(). Il y a par contre le risque d'avoir des warnings de type insolubles, sauf à caster implicitement mais il n'y a plus alors de vérification de types par le compilateur.
    J'ai trouvé un début de réponse.
    Déjà, si les arguments ont la même taille, le type importe peu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void foo(char a);
     
    void (*ptrFoo)(uint8_t a) = (void (*) (uint8_t) )foo;
     
    ptrFoo( 'a' ); // marche
    Donc il faut stocker la taille de chaque argument mais pas leurs type.

    Après, le seul problème c'est qu'il faut stocker les arguments.
    A l'aide d'une fonction de serialisation, c'est tout à fait possible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * serialise(size_t * taille_des_arguments, ...);
    Le seul problème, c'est pour deserialiser les arguments et les passer à la fonction.
    Il me faut une fonction de deserialisation par "paramètres possibles" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    deserialise_8_32_64(args, slot); // appelle slot avec les arguments qui ont 8bits, 32 bits et 64 bits.
    deserialise_16_8_128(args, slot2); // appelle slot2 avec les arguments qui ont 16 bits, 8 bits et 128 bits.
    Je vais donc devoir rajouter à ma macro SIGNAL quelques paramètres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SIGNAL( signal, (bool a, int b), (a,b), deserialise_8_32, {sizeof(bool), sizeof(int) } )
    J'aimerais bien virer le 4 ème argument et trouver la bonne fonction en fonction du 5ème argument...
    Peut-être qu'en remplaçant toutes les fonctions deserialise par une unique fonction... mais il faudrait que je regarde comment je pourrais faire

    Après, je ferais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    signal signal(bool, int);
    Et quand j'exécuterais un petit programme de ma composition, il m'écrira :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SIGNAL(signal, (bool a, int b), (a,b), ????, {sizeof(bool), sizeof(int})
    Donc j'ai presque fini \o/

    EDIT : Le préprocesseur C est tout de même plus que décevant, on a même pas un équivalant à sizeof() pour le préprocesseur
    Peut-être que je vais donner quelques limitations du style : 4 arguments max et 4 tailles possibles

  4. #4
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Je suis en train de penser...

    Pour un prototype donné, et quelque soit le compilateur/OS/options du compilateur, les arguments seront toujours mis aux "mêmes endroits", sinon va_list ne pourra pas fonctionner non-plus. Il est donc théoriquement possible de copier la liste des paramètres de façon générique puis plus tard les remettre en mémoire avant d'appeler la fonction en assembleur avec call.

    Ainsi je n'aurais même pas à m'enquiquiner.
    Faut que je regarde de plus près comment va_list trouve ses arguments.

  5. #5
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Bonjour,

    J'arrive à copier la pile assez simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char * faa(int a, ..., int b)
    {
            static char buffer[4096];
            int taille = sizeof(a) + ((char *)&a - (char *)&b);
            printf("%d\n", taille);
            memcpy(buffer, (void*)&b, taille);
            return &buffer;
    }
    Par contre, pour l'appel j'ai essayé de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Type t = faa(1, ..., 2);
    ((char *)(*)(Type)faa)(t);
    Et ça marche presque.
    Le seul problème, c'est quand certaines variables sont passées dans des registres

    Il faudrait que j'en sache plus sur l'ABI C pour savoir dans quels cas les registres sont utilisés.
    Sinon, je suis obligé de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Type t = faa(1, ..., 2);
    // on extrait les valeurs à partir de t
    ((char *)(*)(int, ..., int)faa)(a, ..., b);
    Ce qui demanderai d'avoir grosso modo une fonction par suite de taille d'argument possibles.

    Personne ne sait ce que dit le standard C au niveau de l'ABI des appels de fonctions ?

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 881
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 881
    Points : 219 325
    Points
    219 325
    Billets dans le blog
    123
    Par défaut
    Bonjour,

    Dans le style violent que vous faisiez avant, pourquoi ne pas sauvegarder les va_list ? et faire une fonction replay() qui prend une va_list directement.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  7. #7
    Membre éclairé
    Profil pro
    Ingénieur sécurité
    Inscrit en
    Février 2007
    Messages
    574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2007
    Messages : 574
    Points : 751
    Points
    751
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char * faa(int a, ..., int b)
    {
            static char buffer[4096];
            int taille = sizeof(a) + ((char *)&a - (char *)&b);
            printf("%d\n", taille);
            memcpy(buffer, (void*)&b, taille);
            return &buffer;
    }
    Le comportement est indefini ici. Tu retournes l'addresse d'un buffer qui est dans une frame de la stack qui a ete depilee par ret. Tu ne peux que retourner buffer par copie, car le compilateur est assez malin pour copier le buffer d'une frame a l'autre. L'OS fait ce qu'il veut de la stack dans ton cas. Par exemple OpenBSD reinitialise la frame a 0 dans ce cas la.

    Citation Envoyé par Neckara Voir le message
    Personne ne sait ce que dit le standard C au niveau de l'ABI des appels de fonctions ?
    Le C ne definit pas d'ABI. C'est le compilateur.
    Copier la pile est extremement non portable, car la pile n'as pas la meme ABI suivant les os (stdcall, cdecl) at entre 32/64 bits. Par experience, sur 32 bits, les syscalls passent les parametres par registres (sous Linux), alors que les fonctions passent par la pile, cf: http://docs.cs.up.ac.za/programming/.../syscalls.html
    Sur 64 bits, tout passe par registre. dans ces cas la, tu peux tout copier sur la stack (en gros copier le pushad 32 bits), mais c'est degueu.

  8. #8
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    Dans le style violent que vous faisiez avant, pourquoi ne pas sauvegarder les va_list ? et faire une fonction replay() qui prend une va_list directement.
    Non, va_list n'enregistre pas les données mais des "pointeurs" sur les données contenues dans la pile.
    Ainsi lorsqu'on quitte la fonction, la pile est dépilée et la va_list ne sert plus à rien.

    Citation Envoyé par dahtah Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    char * faa(int a, ..., int b)
    {
            static char buffer[4096];
            int taille = sizeof(a) + ((char *)&a - (char *)&b);
            printf("%d\n", taille);
            memcpy(buffer, (void*)&b, taille);
            return &buffer;
    }
    Le comportement est indefini ici. Tu retournes l'addresse d'un buffer qui est dans une frame de la stack qui a ete depilee par ret. Tu ne peux que retourner buffer par copie, car le compilateur est assez malin pour copier le buffer d'une frame a l'autre. L'OS fait ce qu'il veut de la stack dans ton cas. Par exemple OpenBSD reinitialise la frame a 0 dans ce cas la.
    Non, buffer est static, il n'est pas stocké dans la stack.


    Le C ne definit pas d'ABI. C'est le compilateur.
    Cela m'étonnerait vu que les bibliothèques dynamiques C peuvent être réutilisé par n'importe quel compilateur, ils ont donc une ABI commune indépendamment du compilateur.

    Copier la pile est extremement non portable, car la pile n'as pas la meme ABI suivant les os (stdcall, cdecl) at entre 32/64 bits.
    Pourquoi l'OS "changerait" la méthode d'appel des fonctions ?
    Tu ne confondrais pas OS et fondeur ?
    Par contre oui, l'ABI "d'appel des fonctions" peut changer selon les jeux d'instructions.

    D'ailleurs, il me semble bien que les bibliothèques dynamiques C peuvent marcher aussi bien sous Linux que sous Windows. (?)
    Pour les 32/64 bits, l'ABI 32 bits est compatible avec l'ABI 64 bits (rétro-compatibilité oblige, mais l'inverse n'est pas vrai).

    Sur 64 bits, tout passe par registre.
    Non, dans certains cas ils sont bien passés par la pile même en 64 bits.

  9. #9
    Membre éclairé
    Profil pro
    Ingénieur sécurité
    Inscrit en
    Février 2007
    Messages
    574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2007
    Messages : 574
    Points : 751
    Points
    751
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Non, buffer est static, il n'est pas stocké dans la stack.
    Autant pour moi. Manque d'attention.

    Citation Envoyé par Neckara Voir le message
    Cela m'étonnerait vu que les bibliothèques dynamiques C peuvent être réutilisé par n'importe quel compilateur, ils ont donc une ABI commune indépendamment du compilateur.
    Non, le C n'a pas besoin de definir une ABI, ce serait trop restrictif.

    Comment est-ce que ton kernel est capable d'executer du code userland? Parce que le kernel et le compilateur se mette d'accord sur une interface commune. L'ABI. Cette ABI est souvent imposee par le format de l'executable (elf, pe, ...). cf http://kernelnewbies.org/ABI
    Je vois pas trop le rapport regardant ton example de lib dynamique. Ta lib est recompilee pour adherer a l'ABI que tu cibles.

    Citation Envoyé par Neckara Voir le message
    Pourquoi l'OS "changerait" la méthode d'appel des fonctions ?
    Parce que un kernel peut implementer plusieurs ABI. cf Linux qui implemente cdecl et syscall a la fois suivant que l'appel est un appel systeme ou "standard".
    D'ailleurs Linux implemente X32_ABI qui permet de faire tourner nativement du code 32 bits sur un processeur 64 bits.


    Citation Envoyé par Neckara Voir le message
    Tu ne confondrais pas OS et fondeur ?
    Oui sans doute. Je confond souvent ma xbox avec ma cafetiere. C'est genant d'ailleur.

    Citation Envoyé par Neckara Voir le message
    Par contre oui, l'ABI "d'appel des fonctions" peut changer selon les jeux d'instructions.
    Non, le processeur n'impose pas d'ABI. Le fondeur peut proposer une ABI, mais rien ne te force a la suivre. C'est comment le kernel utilise le processeur pour faire des appels qui est defini par l'OS. La plupart des OS suivent la convention AMD 64 bits, mais pas Windows, qui implemente sa propre ABI.

    Citation Envoyé par Neckara Voir le message
    D'ailleurs, il me semble bien que les bibliothèques dynamiques C peuvent marcher aussi bien sous Linux que sous Windows. (?)
    Non, car l'ABI differe (stdcall sous Windows vs cdecl sous Linux). Si recompilation pour adherer a l'ABI de l'OS, alors oui.

    Citation Envoyé par Neckara Voir le message
    Non, dans certains cas ils sont bien passés par la pile même en 64 bits.
    Oui, dans certains cas rares. Si ta fonction a plus de 6 arguments, le 7eme est passe sur la stack. En pratique combien de fois a arrive?

  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par dahtah Voir le message
    Non, car l'ABI differe (stdcall sous Windows vs cdecl sous Linux). Si recompilation pour adherer a l'ABI de l'OS, alors oui.
    Mais l'appel de fonction n'est-il pas une instruction assembleur et donc directement interprété par le processeur ?

    Oui, dans certains cas rares. Si ta fonction a plus de 6 arguments, le 7eme est passe sur la stack. En pratique combien de fois a arrive?
    Pour avoir regardé du code assembleur généré avec un gcc 64 bits, je peux te confirmer que ce n'est pas si rare que cela.

  11. #11
    Membre éclairé
    Profil pro
    Ingénieur sécurité
    Inscrit en
    Février 2007
    Messages
    574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2007
    Messages : 574
    Points : 751
    Points
    751
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Mais l'appel de fonction n'est-il pas une instruction assembleur et donc directement interprété par le processeur ?
    L'appel de la fonction est effectivement une instruction asm (call, jmp, ...). Mais cette instruction asm ne definit pas du tout comment sont passes les arguments. Elle ne fait que deplace eip dans la fonction appelee.
    Dans ce cas la, comment se fait le mapping entre les valeurs dans les registres/stack et les arguments des fonctions? Via l'ABI. Pour x86 par exemple, le compilateur et le kernel savent que a ebp+0x4, ebp+0x8,... il y a les arguments de ta fonction et que a ebp - x il y a les variables locales, que eax contient la valeur de retour de la fonction... Le processeur n'a aucun moyen de savoir ca. En gros, comment comprendre le context du call.

    Par exemple, tu pourrais creer ta propre ABI en disant que edi contient la valeur de retour de la fonction, et esi ton premier argument. Tant que tu modifies le compilateur et le kernel, ca marchera.

  12. #12
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 572
    Points
    41 572
    Par défaut
    À ma connaissance, il est impossible d'appeler dynamiquement une fonction en C pur: Il n'y a pas moyen de pousser les paramètres sur la pile, et il n'est même pas permis de "construire" une va_list (des compilateurs vont carrément donner une erreur de compilation sur toute tentative).

    En trichant avec un peu d'asm et les fonctions offertes par Windows, j'ai pu faire ceci (32 bits uniquement) pour passer dynamiquement des paramètres, mais on doit pouvoir faire plus simple si on peut tout mettre dans un bloc (attention aux tailles, alignements etc.) et recopier ce bloc sur la pile dans une fonction en assembleur qui fait l'appel.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  13. #13
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 916
    Points
    17 916
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Bonjour,

    Je recherche à faire des sortes de lambda en C :

    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
    struct {
         void *slot;
         Arguments argument;
    } Lambda;
     
    void foo(int);
    void foo2(char, bool);
    void foo2toi(bool);
     
    Lambda x[3] = { makeLambda(foo, 45),
                            makeLambda(foo2, 'e', true),
                            makeLambda(foo2toi, false) };
     
    // ailleurs dans le code
     
    executer(x[0]); //appelle foo(45);
    executer(x[1]); //appelle foo2('e', true);
    executer(x[2]); //appelle foo2toi(false);
    Est-ce que vous auriez une idée de comment je pourrais faire et comment sauvegarder les arguments ?
    Citation Envoyé par Médinoc Voir le message
    À ma connaissance, il est impossible d'appeler dynamiquement une fonction en C pur:
    Si si ça peut se faire

    C'est tout le fond du principe des fonctions enregistrées (les "callbacks")

    Le tout est d'avoir une définition globale, par exemple :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef int My_Proc(void *client, void *cbstruct);
     
    typedef struct _My_Fct {
     
          int           functiontype ;
          My_Proc   *adr ;
          void         *data ;
     
    } My_Fct ;

    Ensuite, on se fabrique un petit module qui contient les quelques manipulations de base :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     int     My_RegisterFunction                 ( int functiontype,
                                                            My_Proc Adr, void *data )
     
     int     My_RemoveRegisteredFunction    ( int functiontype, 
                                                            My_Proc Adr, void *data )
     
     Boolean My_CheckIfRegisteredFunction  ( int functiontype )
     
     int     My_CallRegisteredFunction          ( int functiontype,
                                                            void *FctStruct )

    Dont par exemple la fonction Call est :

    Code C : 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
     
    int My_CallRegisteredFunction ( int functiontype, void *FctStruct )
    {
    int i, s = ERROR, ss=ERROR ;
     
      if ( Pas_Init == 0 )
         Init_Registered_Functions();
     
       if ( Liste_Fonctions.n_elts == 0 )
          return SUCCESS;
     
       for ( i = 0 ; i < Liste_Fonctions.n_elts ; i++ )
         {
    	if ( ((Liste_Fonctions.elt[i].functiontype == functiontype) ||
    	      (functiontype == ALL_FUNCTIONS)) )
    	  {
    	     ss = Liste_Fonctions.elt[i].adr(Liste_Fonctions.elt[i].data, FctStruct) ;
    	     if ( ss == SUCCESS )
    	       s = SUCCESS ;
    	  }
         }
     
       return s ;
    }


    Là, on peut utiliser directement comme le souhaite Neckara, mais on peut faire encore plus souple, car du coup, on peut se fabriquer soit un fichier .h soit un fichier à lire dynamiquement (le define est plus simple parce qu'on fait référence à des noms qu'on peut formaliser, le fichier à lire va seulement utiliser des chiffres ou des élements entiers d'un tableau)..

    Par exemple si on se définit 3 actions

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #define ACTION1 1
    #define ACTION2 2
    #define ACTION3 3

    Là, on peut soit se définir une structure d'argument générale, soit une structure particulière pour chacun

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct argfoo1 { int i };
    struct argfoo2 { char c ; Boolean b };
    struct argfoo3 { Myfunc func };

    ou

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    typedef int Myfunc ( int i );
    typedef union _My_Value {
     
             int               i ;
             double            d ;
             unsigned char    *b ;
             Myfunc   f ;
    } My_Value ;

    et utiliser un tableau de My_Value pour chacune des fonctions par exemple..

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    My_Value ArgFoo1[1], ArgFoo2[2], ArgFoo3[1];

    Et là, dans son code, on a juste à écrire :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    My_RegisterFunction ( ACTION1, foo1, (void *)ArgFoo1 );
    My_RegisterFunction ( ACTION2, foo2, (void *)ArgFoo2 );
    My_RegisterFunction ( ACTION3, foo3, (void *)ArgFoo3 );

    ou suivant un autre paramère non indiqué là :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    My_RegisterFunction ( ACTION1, foo1, (void *)ArgFoo1 );
    My_RegisterFunction ( ACTION1, foo2, (void *)ArgFoo2 );
    My_RegisterFunction ( ACTION1, foo3, (void *)ArgFoo3 );

    Qui seront appelées via :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
         if ( My_CheckIfRegisteredFunction (ACTION1) )
    	  {
          	     s = My_CallRegisteredFunction ( ACTION1, (void*)ArgFoox );
    	  }

    qu'on peut encore rendre plus souple en définissant un tableau des adresses :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    My_Value **Args :
     
    Args[0] = ArgFoo1 ;
    Args[1] = ArgFoo2 ;
    Args[2] = ArgFoo3 ;




    L'énomre avantage de ça, c'est que une fois le petit module des 4 fonctions faits, on peut s'en servir pour n'importe quoi.

    On peut dynamiquement modifier / enlever / ajouter une fonction, ses paramètres, etc (bien évidemment, en ayant prévu la fonction dans le code).
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 572
    Points
    41 572
    Par défaut
    Le code que tu proposes utilises un prototype statique int(void*, void*).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #15
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 916
    Points
    17 916
    Billets dans le blog
    2
    Par défaut
    et ?

    Oui j'utilise ça, car ça permet de passer n'importe quoi, et ce n'est pas à la fonction "call" ou "register" de savoir.

    C'est à la fonction enregistrée de faire le cast et savoir ce qu'elle va chercher..


    En fait, j'ai découvert - et apprécié la puissance de ça - en utilisant intensivement X11. C'est comme ça qu'est construit la toolkit et Motif (et GTK, wxwidgets, etc, comme aussi Delphi, VB, VC++ etc).


    Quand tu dessines un bouton, avec un IDE interactif tu le "droppes" sur la fenêtre, et tu as une petite fenêtre qui s'affiche avec ses propriétés modifiables... dont par exemple le onClick..

    MAIS ce onClick a déjà des fonctions correspondantes pour ce même widget en interne (par exemple le changement de bordure 3D, pour qu'il apparaisse enfoncé, etc).. Le mécanisme utilisé est ce fameux "Register"... Avec ce genre de structure.. : sur le même objet pour la même action on additionne des callbacks, qui chacune a comme paramètre en général un pointeur sur le wdget, un sur l'événement, et un sur une structrure particulière pour cet objet (un bouton aura un label, une échelle aura un slider, etc). En Delphi (et sans doute en VC++) le pointeur widget est non présent (à cause du "this")

    Alors le style des paramètres est variable, mais de manière générale c'est ce mécanisme, qui est ultra-puissant et hyper-simple...


    En fait, le type que j'ai mis ici est copié sur ce modèle (avec une stucture pouvant dépendre du contexte, équivalent au widget), mais on peut s'en passer et avoir juste un seul paramètre, le Data. Ceci est juste un exemple.. MAis dans le cas de Neckara on peut effectivement se réduire au Data, et alors l'adresse est simplement pasée au moment du Register. Lors du Call, il n'y a pas de paramètre supplémentaire.. les valeurs seront automatiquement prises en compte puisqu'on aura passé l'adresse du tableau de valeurs, qui, elles pourront avoir été modifiées...
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  16. #16
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 916
    Points
    17 916
    Billets dans le blog
    2
    Par défaut
    Pour être plus précis et répondre exactement, voici ce que je ferais :

    Le .h des définitions :

    Code C : 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
     
    typedef union _My_Value {
     
             int      i ;
             char     c ;
             Boolean  b ;
    } My_Value ;
     
    typedef int My_Proc(My_Value **data);
     
    typedef struct _My_Fct {
     
          int        functiontype ;
          My_Proc   *adr ;
          My_Value **data ;
     
    } My_Fct ;
     
    typedef struct _My_FctList {
     
          My_Fct *elt ;
          int     n_elts ;
     
    } My_FctList ;

    Le module des fonctions :

    Code C : 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
     
    static My_FctList          Liste_Fonctions ;
    static int                 Pas_Init = 0 ;
     
    int     My_RegisterFunction         ( int functiontype,
                                          My_Proc Adr, My_Value **data );
    ....
    int     My_RemoveRegisteredFunction ( int functiontype, 
                                          My_Proc Adr, My_Value **data );
    ....
    Boolean My_CheckIfRegisteredFunction ( int functiontype );
    ....
    int     My_CallRegisteredFunction    ( int functiontype );
    {
    int i, s = ERROR, ss=ERROR ;
     
      if ( Pas_Init == 0 )
         Init_Registered_Functions();
     
       if ( Liste_Fonctions.n_elts == 0 )
          return SUCCESS;
     
       for ( i = 0 ; i < Liste_Fonctions.n_elts ; i++ )
         {
    	if ( ((Liste_Fonctions.elt[i].functiontype == functiontype) ||
    	      (functiontype == ALL_FUNCTIONS)) )
    	  {
    	     ss = Liste_Fonctions.elt[i].adr(Liste_Fonctions.elt[i].data) ;
    	     if ( ss == SUCCESS )
    	       s = SUCCESS ;
    	  }
         }
     
       return s ;
    }
    Et dans le main (ou en tous cas la routine de haut niveau) :

    Code C : 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
    My_Value ArgFoo1[1], ArgFoo2[2], ArgFoo3[1];
    ...
    ArgFoo1|0].i = 45 ;
    ArgFoo2[0].c = 'e' ;
    ArgFoo2[1].b = True;
    ArgFoo3[0].b = False;
     
    My_RegisterFunction ( 1, foo1, &ArgFoo1 );
    My_RegisterFunction ( 2, foo2, &ArgFoo2 );
    My_RegisterFunction ( 3, foo3, &ArgFoo3 );
     
    ...
    /* Ailleurs dans le code */
    s = My_CallRegisteredFunction ( 1 );
    ...
    s = My_CallRegisteredFunction ( 2 );
    ...

    Et les fonctions étant par exemple :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int foo1 ( My_Value **data )
    {
    ...
    }


    Mais l'emploi du void* est quand même plus souple pour passer n'importe quoi..
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

Discussions similaires

  1. Réponses: 2
    Dernier message: 11/03/2011, 21h17
  2. Variable dans une fonction appelée par une autre fonction
    Par CyrilD dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 21/12/2010, 13h42
  3. [PHP 5.0] Appel d'une autre fonction de la même classe
    Par Arnaud F. dans le forum Langage
    Réponses: 2
    Dernier message: 11/09/2009, 12h26
  4. Enregistrer des paramètres dans une macro xla
    Par Daejung dans le forum Macros et VBA Excel
    Réponses: 9
    Dernier message: 07/10/2008, 17h29

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