[C++ pour les hommes, les vrais] : les Delegates

: les Delegates [C++ pour les hommes, les vrais] - C++ - Programmation

Marsh Posté le 16-11-2004 à 16:04:22    

(y zy connaissent rien sur blabla, alors je m'adresse directement a l'elite)
 
Messieurs dames, bonjour, le cours du jour sera : comment faire des delegates en C++.
 
Le probleme que nous avons tous rencontré un jour, c'est une fonction demandant une Callback en entrée dans la main gauche, et une fonction membre qui serait parfaite pour ce role dans l'autre.
Drame : ca ne marche pas, car une fonction membre C++ demande en plus le 'this' de l'objet.
 
Que faire ? L'idéal serait un truc a la c#, un pointeur de fonction comprenant aussi le this, mais le tout sous forme d'unique pointeur de fonction. La technique decrite ici, que j'appelerais technique Colgate car j'ai eu l'idée en me brossant les dents, repond a ses attentes grace au "dynamic code generation" que j'ecris en anglais car l'anglais, ca pootre plus.
 
l'idée est d'intercaler entre un pointeur de fonction et la cible un code intermediaire servant a rebondir vers la cible. Ledit code intermediaire comprends simplement deux instructions asm fort banales : mov et jmp.
 

Code :
  1. 0xaddr   mov ecx, thisDeL'objet
  2. 0xaddr2  jmp (addresseFonctionMembre - addr2)


 
C'est tout.
 
Par convention le this est generalement foutu dans ecx, donc on le fait, et on fait un jmp a la place d'un call pour pas changer la pile. le jmp prends une valeur relative donc on la recalcule en live.
 
Le plus rigolo, c'est que, vive le C++, on peut tout balancer ca dans une...structure, sans toucher du tout a l'asm, ou peu.
 

Code :
  1. #pragma pack(1) //Important de faire sauter l'alignement, sinon le code de la structure va etre pourri
  2. struct Delegate
  3. {
  4. public:
  5.   Delegate(void *tThis, void *addrFonction)
  6.   {
  7.      movOpcode1 = ...; //remplacer ici par les bons opcodes, je les ai pas sous la main
  8.      movOpcode2 = ...;
  9.      thisPtr    = tThis;
  10.      jmpopcode  = ...;
  11.      disp = (unsigned int)addrFonction - (unsigned int)(&jmpOpcode);
  12.   }
  13. private:
  14.   unsigned char movOpcode1;
  15.   unsigned char movOpcode2;
  16.   unsigned int  thisPtr;
  17.   unsigned char jmpopcode;
  18.   unsigned int disp;
  19. }
  20. #pragma pack()


 
et voila le travail.
le probleme est de retrouver l'adresse de la fonction membre sous forme de void *, taz avait refilé une interessante solution a coup d'union. On peut meme rajouter une couche de template par dessus, et la, vla le bonheur :

Code :
  1. #pragma pack(1)
  2. template<typename T>
  3. struct Delegate
  4. {
  5. public:
  6.   Delegate(void *tThis, T fonction)
  7.   {
  8.      union truc
  9.      {
  10.         T fonction2;
  11.         unsigned int addr;
  12.      }
  13.      truc machin;
  14.      machin.fonction = fonction;
  15.      movOpcode1 = ...;
  16.      movOpcode2 = ...;
  17.      thisPtr    = tThis;
  18.      jmpopcode  = ...;
  19.      disp = truc.addr - (unsigned int)(&jmpOpcode);
  20.   }
  21. private:
  22.   unsigned char movOpcode1;
  23.   unsigned char movOpcode2;
  24.   unsigned int  thisPtr;
  25.   unsigned char jmpopcode;
  26.   unsigned int disp;
  27. }
  28. #pragma pack()
  29. ...
  30. class A
  31. {
  32. public:
  33.   void prout() {std::cout<<"maman, caca "<<toto;}
  34.   int toto;
  35. };
  36. typedef __stdcall void (*truc) MaFonction;
  37. A *bidule = new A;
  38. Delegate del = new Delegate(bidule, A::prout);
  39. MaFonction chose = (MaFonction) &del;
  40. chose();


 
 
Pour ensuite utiliser ca comme callback, il suffit de caster l'adresse de cette structure en pointeur de fonction (si, si) et vala.
 
enfin j'ai pas encore essayé mais ca devrait marcher au poil.
 
Notez le stdcall. De nombreuses api sont en cdcel, et pas en stdcall, ce qui necessitera un peu de réecriture au niveau du code (passage du mov en autre chose). Je laisse ca en exerice pour le lecteur [:icon7]
 
merci de votre attention


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 16:04:22   

Reply

Marsh Posté le 16-11-2004 à 16:06:07    

J'ai rien compris...[:chacal_one333]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 16-11-2004 à 16:22:13    

euh .... y a des trucs templates qui font ca proprement tu sais :o
 
EDIT : COPITEUR :kaola:
 
http://www.codeproject.com/cpp/SoloGenericCallBack.asp


Message édité par Joel F le 16-11-2004 à 16:27:33
Reply

Marsh Posté le 16-11-2004 à 16:29:16    

copiteur ? mon cul, c'est du 100% trouvé par moi, maintenant la technique dite du "thunkt est pas neuve en elle meme[:itm]


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 16:38:33    

Cette technique s'appelle le thunking et est à la base de l'encapsulation Win32 dans des libs comme OWL ou ATL. Elle ne fonctionne pas sur certains processeurs récents (flag NX) & certains noyaux Linux / XP SP2 car la mémoire allouée n'a pas le droit en exécution...


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 16-11-2004 à 16:48:49    

HelloWorld a écrit :

Elle ne fonctionne pas sur certains processeurs récents (flag NX) & certains noyaux Linux / XP SP2 car la mémoire allouée n'a pas le droit en exécution...


Je lui ai déjà dit, mais il m'a dit qu'il s'en foutait et que depuis qu'il faisait des Delegates Colgate, son sexe avait triplé de volume.
 
Bref, je crois qu'il serait mieux à faire du Java et du SQL, et laisser la vraie programmation aux spécialistes. :o  
 

Reply

Marsh Posté le 16-11-2004 à 17:02:23    

Lam's a écrit :

Je lui ai déjà dit, mais il m'a dit qu'il s'en foutait et que depuis qu'il faisait des Delegates Colgate, son sexe avait triplé de volume.


 
j't'ai repondu :o


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 17:04:38    

Y'a pas de solution portable, déjà à cause de l'asm. Mais en spécifique c'est faisable, à condition d'allouer de la mémoire exécutable avec VirtualAlloc + VirtualProtectEx( PAGE_EXECUTE ).


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 16-11-2004 à 17:10:54    

heink ? boost::function et boost::bind c'est bien plus souple

Reply

Marsh Posté le 16-11-2004 à 17:45:17    

Tu peux filer une boost::function comme callback à une fonction C ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 16-11-2004 à 17:45:17   

Reply

Marsh Posté le 16-11-2004 à 18:01:21    

HelloWorld a écrit :

Y'a pas de solution portable, déjà à cause de l'asm.


 
chui pas trop un mec qui se soucie de la portabilité [:joce]
et ici y'a pu d'asm mais que des opcodes, donc c portable sur tout compatibl eintel x86 :o
 
 

HelloWorld a écrit :


 Mais en spécifique c'est faisable, à condition d'allouer de la mémoire exécutable avec VirtualAlloc + VirtualProtectEx( PAGE_EXECUTE ).


 
bin oui [:spamafote]


Message édité par chrisbk le 16-11-2004 à 18:02:46

---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 18:09:09    

Et les conventions d'appel ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 16-11-2004 à 18:13:33    

HelloWorld a écrit :

Et les conventions d'appel ?


 
comme dit dans le texte, __stdcall  
pour __cdecl va falloir gruger un brin


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 18:16:14    

tu ferais quand même mieux de typer plus fortement
 
Delegate(void *tThis, T fonction)
 
->
 
void Delegate(T * const that, void (T::*function)()

Reply

Marsh Posté le 16-11-2004 à 18:17:37    

Taz a écrit :

tu ferais quand même mieux de typer plus fortement
 
Delegate(void *tThis, T fonction)
 
->
 
void Delegate(T * const that, void (T::*function)()


 
c'etait un exemple vite torché, par contre je conteste le void de retour et mon template avait pas pour but de substituer au type de "this" mais a celui du ptr de fonction


---------------
NP: HTTP Error 764 Stupid coder found
Reply

Marsh Posté le 16-11-2004 à 18:31:43    

HelloWorld a écrit :

Tu peux filer une boost::function comme callback à une fonction C ?


 
Je le fait toute la journée :o

Reply

Marsh Posté le 16-11-2004 à 18:53:13    

tu pourrais poster un exemple ?

Reply

Marsh Posté le 16-11-2004 à 19:03:50    

t'es trop flemmard pour lire leur excellente documentation et leurs exemples ?

Reply

Marsh Posté le 16-11-2004 à 19:07:06    

je trouve pas dans leur excellent exemple une excellente facon de mapper une excellente fonction membre sur une excellente callback type C. Mais faut dire que je suis excellement naze, la

Reply

Marsh Posté le 16-11-2004 à 19:13:02    

chrisbk a écrit :

tu pourrais poster un exemple ?


 
Demain depuis le boulot  [:joel f]

Reply

Marsh Posté le 16-11-2004 à 20:25:30    

mais les callback C, c'est naz ...

Reply

Marsh Posté le 16-11-2004 à 20:26:32    

Taz a écrit :

mais les callback C, c'est naz ...


 
oué d'accord, mais je sais pas si t'as vu, c'est un peu le but de ce bordel d'au dessus

Reply

Marsh Posté le 16-11-2004 à 20:29:28    

mais dans une API C bien foutue, y a toujours un param supplémentaire void* data ...

Reply

Marsh Posté le 16-11-2004 à 20:30:48    

Taz a écrit :

mais dans une API C bien foutue, y a toujours un param supplémentaire void* data ...


 
Certes, mais tu peux rapelle qu'une fonction public pour gerer la callback, alors

Reply

Marsh Posté le 16-11-2004 à 20:38:47    

static tu veux dire

Reply

Marsh Posté le 16-11-2004 à 20:41:38    

Taz a écrit :

static tu veux dire


 
oué non, jme suis empatouillé, j'ai rien dit [:itm]
 
 
 
 

Reply

Marsh Posté le 16-11-2004 à 20:45:29    

emberlificoté tu veux dire

Reply

Marsh Posté le 16-11-2004 à 20:47:26    

je m'empatouille en m'enberfilicotant et c'est mon choix

Reply

Marsh Posté le 16-11-2004 à 20:50:36    

cela dit c'est vraiment du hack
 
quand tu fais du type-punning avec une union, techniquement, ton compilo à le droit d'optimiser et donc ça marcherait pas

Reply

Marsh Posté le 16-11-2004 à 20:52:10    

Taz a écrit :

cela dit c'est vraiment du hack
 
quand tu fais du type-punning avec une union, techniquement, ton compilo à le droit d'optimiser et donc ça marcherait pas


 
Oui, mais comme tu vois, j'en suis pas un hack pres, alors si pour retrouver la valeur fo passer par un memcpy crado ou un coup d'asm inline, ca me fait pas peur [:franck75] (l'union marche tres bien, merci)

Reply

Marsh Posté le 16-11-2004 à 21:14:58    

chrisbk a écrit :


Le probleme que nous avons tous rencontré un jour, c'est une fonction demandant une Callback en entrée dans la main gauche, et une fonction membre qui serait parfaite pour ce role dans l'autre.
Drame : ca ne marche pas, car une fonction membre C++ demande en plus le 'this' de l'objet.


personnellement, je contourne le problème en déclarant la fonction en static [:joce]


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 16-11-2004 à 21:16:49    

Harkonnen a écrit :

personnellement, je contourne le problème en déclarant la fonction en static [:joce]


 
heuh, merci, hans [:petrus75]
ca resouds pas tout hein ? :o

Reply

Marsh Posté le 16-11-2004 à 21:32:31    

chrisbk a écrit :

heuh, merci, hans [:petrus75]
ca resouds pas tout hein ? :o


ben non, le problème est que je ne peux plus accéder aux données membres non static de ma classe.
mais dans ce cas, je file en paramètre à la Callback le pointeur this, et j'appelle cette Callback à partir d'une fonction static (qui est en fait la vraie callback) :spamafote:


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 16-11-2004 à 21:34:56    

Harkonnen a écrit :

ben non, le problème est que je ne peux plus accéder aux données membres non static de ma classe.
mais dans ce cas, je file en paramètre à la Callback le pointeur this, et j'appelle cette Callback à partir d'une fonction static (qui est en fait la vraie callback) :spamafote:


 
methode taz de plus haut ca :o
 
mais spa tjs possible (genre les callback des fenetres windows. A part foutre dans le GWL_USERDATA en priant pour que personne vienne foutre sa merde, t'as une solution fine ?)

Reply

Marsh Posté le 16-11-2004 à 22:49:42    

chrisbk a écrit :


mais spa tjs possible (genre les callback des fenetres windows. A part foutre dans le GWL_USERDATA en priant pour que personne vienne foutre sa merde, t'as une solution fine ?)


oui, je balance le this dans les propriétés des fenêtres, SetProp et GetProp roulaize :o
http://msdn.microsoft.com/library/ [...] erties.asp
 
edit: en plus, le GWL_USERDATA c'est le mal, car pour une fenêtre ou un controle que tu n'as pas créé toi même, tu peux pas être sur qu'il ne soit pas déja "occupé"


Message édité par Harkonnen le 16-11-2004 à 22:51:04

---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 16-11-2004 à 22:58:17    

Poste inutile du gars qui se dit que ca doit etre bien de comprendre ce que vous racontez...le seul truc que g pigé parce que je le savais déjà, c qu'en static t'a pas accès aux variables d'instances (ce qui est la logique meme de la chose)...


---------------
Jubi Photos : Flickr - 500px
Reply

Marsh Posté le 16-11-2004 à 23:04:07    

tu veux comprendre quoi ? t'as deja fait du C++ ?

Reply

Marsh Posté le 16-11-2004 à 23:18:29    

Jubijub a écrit :

Poste inutile du gars qui se dit que ca doit etre bien de comprendre ce que vous racontez...le seul truc que g pigé parce que je le savais déjà, c qu'en static t'a pas accès aux variables d'instances (ce qui est la logique meme de la chose)...


bah, stout simple :??:
la problématique est la suivante : une fonction callback est une fonction appelée par le système en réponse à un évènement précis. cette fonction doit respecter une certaine signature.
le problème avec le C++, c'est que chaque méthode de classe possède un premier argument caché : le pointeur de l'objet (le this). donc, la signature de la callback n'est pas respectée, et ça ne marche pas !
les seules fonctions C++ qui ne possèdent pas de this en 1er paramètres, sont les fonctions static. malheureusement, tu ne peux pas accéder aux données membres à partir des fonctions static.
d'où la bidouille du père bk (bidouille qui me rappelle certains hacks du temps ou je codais des démos, quand on se faisait chier à calculer des adresses relatives pour éviter de foutre en l'air le registre de pile, et pour gagner quelques cycles :D)


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 16-11-2004 à 23:21:14    

spa une bidouille, stun thunk [:mmmfff]
et ton compilo C++ t'en genere plein sans que tu le voyes :o

Reply

Marsh Posté le 16-11-2004 à 23:26:38    

c'est pas ça du tout. les fonctions membres non-static ne sont tout simplement pas des fonctions comme les autres fonctions libres. Si y avait que cette histoire de this implicite, on se marrerait bien.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed