2010-11-04 5 views
-1

Je recherche une solution pour appeler la fonction virtuelle de façon dynamique. Cela signifie prendre en charge les appels de fonction avec des décalages et des types dynamiques lors de l'exécution.C++: Appel de fonction virtuelle dynamique

Je travaille sur un plugin pour un jeu (Counter-Strike: Source, si vous le savez). L'interface du plugin est peu probable et vous ne pouvez donc pas étendre autant que la plupart des gens le souhaitent. Pour réaliser une meilleure façon de communiquer et de manipuler le jeu, je supporte l'appel de fonctions virtuelles. Par exemple, la classe CCSPlayer a ces méthodes:

Offset Name 
... 
201  CCSPlayer::Ignite() 
... 
205  CCSPlayer::CommitSuicide(bool Blah) 
... 
250  CCSPlayer::SomeFunctionWith2Params(int A1, float A2) 

Passing ces décalages et le pointeur sur l'instance de cette classe à ma fonction actuelle (voir ci-dessous), je peux appeler cette fonction:

CBasePlayer *pPlayer = Players.Find("mike"); 
bool bResult = Call2<bool, int, float>(210 /* offset */, pPlayer, 20, 50.0f); 

Je fais ceci pour appeler les fonctions virtuelles que je ne peux pas appeler à travers la routine normale parce que le compilateur ne connaît pas la structure de la classe CCSPlayer dérivée.

Et maintenant je veux ajouter un langage de script avec des appels de fonctions virtuelles dynamiques. Cela signifie que les scripteurs peuvent définir la quantité de params, et de quel type ils sont. Puis en passant le this-pointeur et le décalage à la fonction, pour finalement l'exécuter. C'est mes vraies questions.

Actuellement, je ne peux que coder en dur ceux-ci en utilisant des modèles et en créant une fonction pour chaque quantité de paramètres. Exemple:

template<typename T, typename A, typename B> T Call2(int iOffset, void *pThis, A arg1, B arg2) 
{ 
    void **pVTable = *(void***)pThis; 
    void *pPointer = pVTable[_iOffset]; 

    union 
    { 
     T (CVCallEmpty::*pCall)(A, B); 
     void *pAddress; 
    } Data; 

    Data.pAddress = pPointer; 

    return (reinterpret_cast<CVCallEmpty*>(*(void***)&pThis)->*Data.pCall)(arg1, arg2); 
} 

Maintenant, est-il possible de prendre en charge les appels dynamiques?

+0

Pas de méthode primitive – Sheen

+2

Pourriez-vous expliquer ce que vous voulez dire en appelant dynamiquement des fonctions virtuelles? ? Le code a l'air horrible. –

+3

Quelle est la raison de ce überhack? Qu'essayez-vous vraiment de faire? – Dialecticus

Répondre

3

Ceci est tellement brisé, il est difficile de penser à l'étendre.

Vous ne gérez pas l'héritage virtuel. Vous ne gérez même pas l'héritage multiple.

Une fonction virtuelle ne peut pas être représentée comme un entier "offset" unique. Vous devez accepter un argument pointeur vers membre.

+0

+1: le pointeur-à-membre gère correctement la virtualité ET assure la sécurité du type en même temps, alors pourquoi réinventer la roue? –

1

Comme une autre affiche a dit cependant, il n'y a pas native façon d'effectuer cela en C/C++. Cependant, vous pourriez être intéressé par la bibliothèque FFCALL, ou plus précisément avcall. Cette bibliothèque vous permet de créer des listes de paramètres, puis d'y appeler des fonctions.

0

Il n'existe pas de méthode intégrée pour générer les permutations. Vous pouvez utiliser une bibliothèque, j'imagine, mais je recommande d'écrire un petit programme pour générer tous les modèles pour vous. Cela vous donne un contrôle total sur le code source généré, il n'y a pas d'indésirable, et il est facile de faire un seul pas. Un problème avec l'approche présentée est que les types de paramètres pour la fonction pointée sont déduits des arguments fournis; les types de paramètres doivent vraiment être spécifiés indépendamment. En l'état, vous pourriez (disons) passer un flottant dans une fonction en attendant un int, et obtenir des résultats invalides (au mieux). L'appelant devra être très précis avec leurs valeurs! Si vous avez votre programme de génération de code source, vous pouvez modifier légèrement pour générer des fonctions de transfert, le long des lignes de:

inline void call_vii(int vi,void *obj,int a0,int a1) { 
    Call2<void,int,int>(vi,obj,a0,a1); 
} 

inline float call_viff(int vi,void *obj,int a0,float a1,float a2) { 
    Call2<void,int,float,float>(vi,obj,a0,a1,a2); 
} 

Et ainsi de suite, ad infinitum (ou non loin).

Ce n'est pas vraiment très pratique, bien sûr. Et cela génère beaucoup de fonctions. Mais si vous allez exposer cela à un langage de script, vous aurez besoin d'une fonction pour chaque combinaison de types de paramètres de toute façon ...