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++/CLI Discussion :

recoder MethodInfo->Invoke avec il.Emit


Sujet :

C++/CLI

  1. #1
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut recoder MethodInfo->Invoke avec il.Emit
    Bonjour,
    Quelqu'un a d�j� utilis� du il.Emit pour g�n�rer des m�thodes au runtime ?
    Il faut forc�ment un d�l�gate pour appeler la m�thode ainsi g�n�r�e ?
    En C++/CLI je peux aussi r�cup�rer un pointeur de m�thode, non ? (avec InteropServices.Marshal.GetFunctionPointerForDelegate ou MethodInfo RuntimeMethodHandle.GetFunctionPointer ?)

    Ici je voudrais faire un g�n�rateur dynamique de stub interpr�t�->compil�, c'est � dire un stub qui permet appeler une m�thode en lui passant un tableau de param�tres (donc exactement ce que fait MethodInfo.Invoke mais en plus efficace en terme de perf vu que MethodInfo.Invoke fait plein de choses "inutiles", alors qu'un stub ne fait que 10 instructions MSIL) :

    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
     
    /// classe et méthode compilée
    ref class A {
     String^ UneMethode(Int32 k, array<Type^> t) {
        /* ... */
     }
    };
     
    /// classe qui génère des stub
    ref class StubFactory {
       D^ GenerateStubFor(MethodInfo ^m) {
          il.Emit(...)
          il.Emit(...)
       }
     
       /// méthode générée dynamiquement avec il.Emit
       static Object^ A_UneMethode_Stub(Object^ obj, array<Object^>^params) {
           // implémentation en C++/CLI en guise d'exemple, c'est en MSIL qu'il       
          //faut savoir la coder pour pouvoir la générer dynamiquement
          return (*((A^*)&obj))->UneMethode((Int32)params[0],(array<Object^>^)params[1]);
          /// le cast *((A^*)&obj)) est un static_cast : il permet d'éviter le dynamic_cast qui fait le runtime check du type (qui est coûteux si on sait déjà à l'avance que le type est bon). 
          /// Pour params[0] et params[1] je pense qu'on ne peut pas y échapper.
       }
    };
    Je m'�tonne de ne pas trouver sur google quelqu'un qui aurait d�j� cod� la m�me chose.

    Je pense que StubFactory.GenerateStubFor devrait renvoyer un D^ qui serait un type de delegate :
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    delegate Object ^ D(Object^ obj, array<Object^>^params);
    Ensuite, j'aimerais aussi faire la m�me chose pour PropertyInfo/FieldInfo.Get/Set Value.

    Une derni�re question : c'est compliqu� d'appeler une m�thode C++ native __stdcall depuis du IL g�n�r� avec il.Emit ?
    Je pense m'en sortir en utilisant ILdasm sur des bouts de code C++/CLI tr�s simple me servant d'exemple

    Une toute derni�re question : est-ce qu'il faut vraiment g�n�rer dynamiquement (avec il.emit) un stub par m�thode, ou est-ce qu'il serait possible d'�crire, statiquement, donc sans passer par il.emit, une seule m�thode (enti�rement �crite en MSIL) qui pour n'importe quel type de m�thode arrive � pousser le tableau de param�tre sur la pile et � faire le call ?

    A mon avis pour �crire un champ d'un objet on est oblig� de passer par il.Emit (il n'y a aucune fonction de la r�flection qui donne l'offset runtime d'un champ, donc impossible de faire une fonction g�n�rique capable d'�crire/lire un champ)

  2. #2
    Expert �minent
    Avatar de M�dinoc
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par d�faut
    J'ai d�j� utilis� System.Reflection.Emit pour cr�er des getters/setters autour de champs (afin d'obtenir des delegates, bien plus performants que FieldInfo.SetValue()), mais c'est tout. Et c'�tait en C#.

    Aussi, d'apr�s mes travaux de l'�poque, il me semblait que C++/CLI n'autorisait pas les pointeurs de membre vers des types manag�s.

    PS: Je suppose que MethodInfo.Invoke() et Delegate.DynamicInvoke() sont �quivalents et donc, tout aussi pauvres en performance?

    PPS: Lire/�crire chaque champ d'un objet est beaucoup plus facile que faire des proxys de fonctions non-manag�es, car la signature est fixe et le code enti�rement manag�.
    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.

  3. #3
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut
    �a y est premi�re version de DynamicMethod_Invoker utilisant ILGenerator.Emit !

    Il permet d'appeler dynamiquement (donc en passant un tableau d'Object^ comme param�tres) la plupart des "fonctions" .NET

    • type de fonction:
      1. m�thode normale, virtuelle, statique
      2. constructeur
      3. field getter/setter/address (il est pr�f�rable de pin_ptr l'objet contenant le champ avant d'en r�cup�rer l'addresse)
      4. property getter/setter
      5. m�thode/constructeur de value type (pour l'argument this : passage du value type par r�f�rence)

    • type des param�tres :
      1. reference type (class, delegate, array, interface, boxed type)
      2. value type (dont type primitif), pointeur natif
      3. tracking reference

        • variable locale, param�tre, gcroot (instruction ldloca)
        • �l�ment de tableau (instruction ldelema)
        • champ de classe ou de value type (instruction ldflda / ldsflda)


    Pour prendre en charge les m�thodes renvoyant une tracking reference, il faudrait un type de delegate ne renvoyant pas un Object^ mais un T% (ce qui va � l'encontre du c�t� dynamique puisqu'on doit stocker ce T% dans une variable typ�e statiquement T%). Une solution serait de renvoyer un Object^% ou un Int32%.
    On peut pinner n'importe quelle tracking r�f�rence (dont on ne sait pas si elle est � addresse statique, de tableau, ou de champ) et l'envoyer dynamiquement � une m�thode.

    Pour les gcroot, il se trouve qu'apr�s inspection du getter GCHandle.Target des versions 32/64bit de .NET (4.x), un GCHandle normal est directement un Object^*. Pour les GCHandle pinned ou weak c'est un Object^* avec un flag dans les 4 premiers bits qu'il faut enlever avant la conversion en Object^* qui peut �tre converti en Object^%, ou pass� dynamiquement � une m�thode avec mon type TrackingReference_Ldelema.

    � faire : permettre d'appeler dynamiquement une fonction priv�e (il suffit que la DynamicMethod soit d�clar�e comme m�thode statique du type � ce que j'ai compris).
    Fichiers attach�s Fichiers attach�s

  4. #4
    Expert confirm�
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    F�vrier 2005
    Messages
    5 504
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : France, Val de Marne (�le de France)

    Informations professionnelles :
    Activit� : D�veloppeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : F�vrier 2005
    Messages : 5 504
    Par d�faut
    GG.

    T'as des benchmark pour connaitre le gain ?

  5. #5
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut
    Salut, le but c'est un gain en perf mais c'est surtout de pouvoir appeler dynamiquement une m�thode qui a des param�tres par r�f�rence (si on oublie �a on peut se passer de IlGenerator et coder tout en g�n�riques), ou encore de r�cup�rer l'adresse (ou un interior_ptr) sur un champ d'un objet (pour les champs IlGenerator est indispensable, quoiqu'il y a aussi le hack de GetFieldOffset que j'ai expliqu� dans un autre post).

    Pour les perfs �a a d�j� �t� pas mal discut� (voir fasterflect par exemple)

    m�thode simple avec 3 param�tres fabriqu�s avant les 10000000 appels en boucle, donc on prend en compte surtout la lenteur du protocole d'appel et des checks

    Direct : 00.5990343
    Delegate.Invoke : 00.6010344
    DynamicDelegate.Invoke : 00.7240414 <---
    Delegate.DynamicInvoke : 11.9426916
    MethodInfo.Invoke : 05.4323108


    ( les 3 derniers ont un prototype d'appel dynamique avec un array<Objet^> )

    Je trouve �a hallucinant que .NET ne propose pas un dynamicInvoker dans l'esprit de celui que j'ai fait, avec peut-�tre plein d'options pour int�grer des checks des param�tres plus ou moins co�teux et safe.
    Je reproche �galement � .NET de ne pas proposer de m�thode
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    Object^% GCHandle::getTrackingReference
    Et on ne peut pas allouer de tracking reference (interior_ptr) autre part que sur la pile, il y a une raison valable pour �a ?
    Il y a aussi le manque de propri�t� Type.IsReallyValueType pour distinguer les value-type avec ou sans Object^ (dans le second cas on peut les allouer dans le tas non manag�).
    Ou le fait qu'on ne peut pas d�bugger avec visual le code IL d'une DynamicMethod. M�me le code assembleur est invisible (sauf en faisant DebugBreak() en native mode debug mais bon..).
    Et les value-types boxed, pourquoi il n'y a pas de moyen d'avoir une propri�t� pour acc�der au value-type cach� � l'int�rieur (vous avez d�j� essay� de modifier la valeur � l'int�rieur d'un Int32^ ?).

    Quand on fouille un peu on se rend compte que .NET n'est pas si s�rieux que �a, et pourtant il manque si peu !

  6. #6
    Expert �minent
    Avatar de M�dinoc
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par d�faut
    Pour les types valeur box�s, je pense qu'ils sont volontairement immuables (comme les classes wrappant les types primitifs en Java), pour garder leur s�mantique de valeur.
    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.

  7. #7
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut
    je sais pas trop,
    s�mantiquement les types boxed sont plus ou moins

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
     
    generic<typename U> where U : value class
     ref struct Box {
        U u;
        Type GetuType() { return U::typeid; }
    };
    Si le "U u" doit �tre pens� comme const, pourquoi peut-on acc�der � ses champs en �criture, et appeler des m�thodes non const dessus ?

    pour Int32^ (qui n'a ni champ ni m�thode non const) c'est clairement �quivalent �
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
     
    ref struct BoxInt32 {
        const Int32 u;
        Type GetuType() { return Int32::typeid; }
    };
    en IL il y a l'instruction Opcodes::Box qui prend un handle boxed et push l'adresse de la valeur : l'instruction IL correspond donc parfaitement � un objet qui contient une valeur accessible en �criture.

    � mon sens tout cela n'est donc pas tr�s coh�rent.

  8. #8
    Expert �minent
    Avatar de M�dinoc
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par d�faut
    � ma connaissance, les types valeur box�s ne sont accessibles en tant que tels qu'en C++/CLI et en CIL.
    En C#, ils sont compl�tement immuables, car le seul acc�s possible est "lire la valeur pour la mettre dans le vrai type" (via unboxing cast, ou Convert.ToXxxx(object)).

    Je consid�re C++/CLI comme un cas un peu "� part" qui permet de faire certains trucs non-orthodoxes.
    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.

  9. #9
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut
    En fait je bosse sur une sorte de Pascal.NET interpr�t� et compil� (je rajoute juste le .NET, et juste la consommation, on ne peut pas cr�er de types � part des types de delegates).
    Pour les values types j'ai d�cid� de faire comme �a :

    Si on d�clare une variable de type .NET value type, alors c'est un handle vers le type boxed qu'on d�clare, on ne peut pas mettre un value type directement dans la pile comme en C# ou C++/CLI.

    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
     
    /*supposons qu'on a un assembly avec un value type :*/
    value struct XY {
        Int32 x,y;
        void ChangeX(Int32 _x) { x = _x; }
    };
     
    /// ----------------
    /// code pascal.NET
    /// ----------------
    var k : int // c'est un int natif (du langage Pascal) et donc sur la pile 
    var u : Int32 // c'est un Int32^, donc juste un handle, à null pour le moment
    var xy : XY // c'est un XY^, à null pour le moment
     
    u = nil // c'est un handle donc on peut le mettre à null
    xy = nil // c'est un handle donc on peut le mettre à null
     
    u = 8 // construit un nouveau boxed Int32 (donc dans le managed heap)
    u = u + 2 // correspond à    u = int(u) + 2 avec la convertion implicite boxed Int32 vers int  
                  // donc ça construit un nouveau boxed Int32 de valeur 10
     u = int(u) + 2 // est valide aussi
                         // on a donc encore construit un nouveau boxed Int32 de valeur 12
     
    u = u + u // erreur sémantique au parsing : pas d'opérateur + sur les types .NET, uniquement sur les types Pascal
     
    u = nil
    k = u // erreur à l'exécution car u est null
     
    xy = gcnew XY() // construit un nouveau boxed value type
     
    xy.x = 9 // change juste la valeur du champ
    u = 19
    xy.x = u // on change à nouveau la valeur du champ donc sans réallouer d'objet XY
    xy.ChangeX(10) // appelle la méthode en passant xy (comme argument this) par référence
                           // donc ici xy.x vaut bien 10
     
    xy.x = nil // erreur sémantique : on ne peut pas mettre à null un value type qui n'est pas une variable locale

    • Les gros avantages :
      • � premi�re vue c'est comme en Java : les value type n'existent pas, et les conversions implicites entre le type primitif et son �quivalent boxed ( le type int Pascal <-> le type Int32 Pascal.NET qui correspond � Int32 boxed ) permettent de tout faire (je rappelle qu'aucun op�rateur n'est d�fini sur Int32 et autres, uniquement sur les types primitifs Pascal).
      • Les value types que le d�veloppeur d�clare comme variables locales, comme tous les objets .NET qu'il peut d�clarer, sont garbage collect�s.
    • Les gros d�savantages :
      • le d�veloppeur doit accepter ou comprendre que d�clarer des Int32 et faire des calculs dessus est co�teux en perf : il est pr�f�rable d'utiliser le type Pascal natif (int)
      • xy.x = nil donne une erreur s�mantique au parsing, et k = Int32(nil) donne une erreur � l'ex�cution,
        donc le d�veloppeur doit apprendre ce que sont les value types pour comprendre �a. Mais il peut aussi facilement esquiver la question..
      • Un d�veloppeur C# ou C++/CLI doit comprendre ce concept de value types uniquement manipul�s par handle, concept qui n'existe pas du tout en C#.



    En fait si j'avais donn� aux value types une s�mantique de pile comme en C#, le probl�me se serait pos� quand le d�veloppeur aurait voulu d�clarer un value type dans un objet Pascal : si le value type ne contient que des Int32 et autres c'est bon, mais si le value type contient des Object^ alors le garbage collector doit les conna�tre (ce qui oblige � passer par un gcroot et un value type boxed) ce qui rend donc le langage aussi compliqu� que C++/CLI : il y a les types primitifs, les value types ne contenant que des types primitifs, les objets .NET, les objets Pascal, et les value types qui contiennent des handle ! Je pense que ma solution est plus simple � appr�hender (tout objet .NET est garbage collect�) et permet grosso-modo de faire les m�mes choses c'est � dire instancier/r�f�rencer des objets .NET dans des objets natifs (Pascal) et appeler des m�thodes dessus.

  10. #10
    Expert �minent
    Avatar de M�dinoc
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par d�faut
    En fait ce que tu appelles Pascal.Net, c'est plus une couche entre un interpr�teur Pascal classique et le Framework, plut�t qu'une version de Pascal qui compile directement en CIL et repose enti�rement sur le Framework?
    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.

  11. #11
    Membre tr�s actif

    Profil pro
    �tudiant
    Inscrit en
    D�cembre 2004
    Messages
    499
    D�tails du profil
    Informations personnelles :
    �ge : 39
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2004
    Messages : 499
    Par d�faut
    Citation Envoy� par M�dinoc Voir le message
    En fait ce que tu appelles Pascal.Net, c'est plus une couche entre un interpr�teur Pascal classique et le Framework, plut�t qu'une version de Pascal qui compile directement en CIL et repose enti�rement sur le Framework?
    en interpr�t� oui, en compil� c'est transform� en C++/CLI donc ensuite en CIL.

Discussions similaires

  1. Utilise p/invoke avec un lib static
    Par teddyalbina dans le forum Framework .NET
    R�ponses: 0
    Dernier message: 21/06/2011, 00h13
  2. [Prototype] Avoir plusieurs "invoke"
    Par brunoperel dans le forum Biblioth�ques & Frameworks
    R�ponses: 1
    Dernier message: 30/07/2008, 10h36
  3. Method invoke avec List<generics>
    Par youx21 dans le forum Langage
    R�ponses: 4
    Dernier message: 06/12/2007, 09h11
  4. Invoke avec arguments null
    Par Shiftane dans le forum API standards et tierces
    R�ponses: 1
    Dernier message: 28/06/2006, 10h51
  5. pb avec invoke de java.lang.reflect.
    Par sebastien2222 dans le forum API standards et tierces
    R�ponses: 2
    Dernier message: 28/04/2006, 13h33

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