2009-11-15 4 views
3

Dans un C lib, il y a une fonction d'attente d'un pointeur de fonction telle que:C++ Utilisation de la méthode de classe comme un type de pointeur de fonction

lasvm_kcache_t* lasvm_kcache_create(lasvm_kernel_t kernelfunc, void *closure) 

où lasvm_kernel_t est défini comme:

typedef double (*lasvm_kernel_t)(int i, int j, void* closure); 

Maintenant, si j'envoie une méthode définie dans une classe à lasvm_kcache_create:

double cls_lasvm::kernel(int i, int j, void *kparam) 
... 
lasvm_kcache_t *kcache=lasvm_kcache_create(&kernel, NULL); 

je reçois: « ne peut convertir « double (cls_lasvm :: ) (int, int, vide) » à « double () (int, int, vide) » »

Que dois-je faire?

Répondre

0

Chaque fonction membre C++ possède un premier paramètre implicite, caché, this. Donc la méthode double cls_lasvm::kernel(int i, int j, void* kparam) est vraiment: double cls_lasvm::kernel(cls_lasvm* this, int i, int j, void* kparam), et il est inapproprié/impossible de l'utiliser comme un paramètre de pointeur de fonction. Pour faire des progrès, convertissez votre méthode en une méthode statique-membre. Cela supprimera le pointeur this. Vous pouvez encore avoir d'autres problèmes à surmonter, mais c'est un bon début.

+1

Je ne pense pas qu'il y ait un "premier paramètre" caché implicite: vous pourriez tout aussi bien dire qu'il a un "dernier paramètre" caché implicite. La valeur 'this' est disponible dans l'appelé, c'est tout. Donc ce n'est pas "vraiment" ce que vous avez dit - essayez de changer 'kernelfunc' pour ce type, et le code ne compilera toujours pas. pointer-to-function et pointer-to-member-function sont des choses différentes. –

+0

@Steve: Je pense qu'il est généralement défini comme le premier paramètre, en fait. Cette distinction fait la différence, puisque d'autres fonctions (et d'autres langages, je suppose) ont besoin de savoir dans quel ordre passer ces paramètres. –

+0

__Ne pas utiliser la méthode des membres statiques pour les méthodes de rappel C. Il n'est pas garanti de fonctionner car la norme C++ ne spécifie pas délibérément un ABI. –

0

S'il s'agit d'une bibliothèque C externe dont vous ne pouvez pas modifier le code, alors vous ne pouvez rien y faire. Vous ne pourrez pas appeler la fonction membre car ils ont besoin du pointeur this pour fonctionner correctement (pour obtenir les attributs de l'objet). La solution de contournement la plus simple, je peux penser à utiliser le troisième void* param pour passer autour de this pointeur. Vous pouvez définir comme struct après avoir défini un plus typedef comme:

typedef double (cls_lasvm::*lasvm_kernel_t_member)(int i, int j, void* closure); 


struct MyParam 
{ 
    A* pThis; 
    lasvm_kernel_t_member pMemFun; 
    void* kParam; 
}; 

Je n'ai pas compilé, je l'espère, il est logique.

Ensuite, dans votre classe définit une méthode statique qui reçoit l'appel de la bibliothèque:

class cls_lasvm 
{ 
    static double test(int i, int j, void *kparam) 
    { 
    MyParam* pParam = reinterpret_cast<MyParam*>(kparam); 
    return (pParam->*pMemFun)(i,j,pParam->kParam); 
    } 
}; 

Pendant que vous appeler devez utiliser quelque chose comme:

cls_lasvm a; 
MyParam param; 
param.pThis = &a; 
param.pMemFun = &cls_lasvm::kernel; 
param.kParam = NULL; 

lasvm_kcache_create(&cls_lasvm::test,&a); 
5

Je suppose que l'argument est closure un contexte 'cookie' pour l'utilisation du rappel pour obtenir le contexte approprié. Ceci est un idiot d'acomon pour les fonctions de rappel, et semble être ce qui se passe sur la base des extraits que vous avez fournis (mais je ne sais pas avec certitude, car je ne sais rien sur kcache_create() sauf ce que vous avez posté ici).

Vous pouvez utiliser ce cookie pour passer un pointeur vers le vous par exemple cls_lasvm traiter comme ceci:

extern "C" 
double 
lasvm_kcache_create_callback(int i, int j, void* closure) 
{ 
    // have to get a cls_lasvm pointer somehow, maybe the 
    // void* clpsure is a context value that can hold the 
    // this pointer - I don't know 

    cls_lasvm* me = reinterpret_cast<cls_lasvm*>(closure); 

    return me->kernel(i, j) 

} 


class cls_lasvm //... 
{ 

    ... 

    // the callback that's in the class doens't need kparam 
    double cls_lasvm::kernel(int i, int j); 

}; 

... 

// called like so, assuming it's being called from a cls_lasvm 
// member function 

lasvm_kcache_t *kcache=lasvm_kcache_create(&lasvm_kcache_create_callback, this); 

Si je me trompe fermeture étant un cookie contexte, votre fonction de rappel dans la cls_lasvm classe doit être statique:

extern "C" 
double 
lasvm_kcache_create_callback(int i, int j, void* closure) 
{ 
    // if there is no context provided (or needed) then 
    // all you need is a static function in cls_lasvm 

    return cls_lasvm::kernel(i, j, closure); 
} 

// the callback that's in the class needs to be static 
static double cls_lasvm::kernel(int i, int j, void* closure); 

Notez qu'une fonction de rappel C implémenté en C++ doit êtreextern "C". Cela peut sembler fonctionner comme une fonction statique dans une classe car les fonctions de classe-statique utilisent souvent la même convention d'appel qu'une fonction C. Cependant, faire cela est un bug qui attend de se produire (voir les commentaires ci-dessous), donc s'il vous plaît ne faites pas - passer par un wrapper extern "C" à la place.

Si closure n'est pas un cookie de contexte et pour une raison quelconque cls_lasvm::kernel() ne peut pas être statique alors vous devez trouver un moyen de cacher un pointeur this quelque part et récupérer ce pointeur dans la fonction lasvm_kcache_create_callback(), semblable à la façon Je l'ai fait dans mon premier exemple, sauf que le pointeur doit provenir d'un mécanisme que vous vous imaginez. Notez que cela va probablement utiliser lasvm_kcache_create() non-réentrant et non-threadsafe. Cela peut ou peut ne pas être un problème en fonction de vos circonstances spécifiques.

+1

Malheureusement, j'ai été mordu par la méthode statique n'ayant pas la même convention d'appel que la fonction «C». Et vu la façon dont C + = les compilateurs se déplacent, je doute que cela reste vrai. Les compilateurs hautement optimisés vont tirer parti de toutes les techniques possibles pour obtenir de la vitesse et ainsi passer les paramètres dans le registre au lieu que la pile commence à devenir de plus en plus commune (ce n'est qu'une question de temps). –

+0

@Martin: OK - Je vais supprimer la suggestion qu'il est susceptible d'être sûr. Man - il y a un * lot * de code qui va se casser si/quand ces optimisations (ou quoi que ce soit) deviennent banales. Je suppose que plus de 75% du temps que je vois les callbacks C utilisés dans le code C++ est fait directement par une fonction membre statique. –

+0

@Michael: Je le vois de temps en temps avec des codeurs plus jeunes. Mais dans les grandes entreprises, ces erreurs se terminent toujours par une révision de code (et une fois tousser, l'erreur n'est pas répétée car nous pouvons la rendre assez embarrassante).J'ai donc vu (pas beaucoup) dans les projets open source que les têtes sages ne dénigrent pas la jeune génération. –

Questions connexes