2009-11-29 4 views
4

J'ai une commande std :: map que j'essaie de stocker des pointeurs vides pour les valeurs. Le problème est que la plupart des pointeurs que j'essaie de stocker sont des méthodes dans une classe et ont une quantité différente de paramètres. Je sais pour les paramètres que je peux utiliser une liste va donc ce n'est pas trop un problème, le problème serait le pointeur lui-même.Passer le vide (*) en C++

C'est ce que j'ai:

class A 
{ 
public: 
    A(); 
    void methodA(...); 
}; 
class B 
{ 
public: 
    B(); 
    void methodB(...); 
}; 
void method_no_class(...) { } 

std::map<int, void(*)(...)> my_map; 

my_map[0] = &method_no_class; 
B* cb = new B(); 
my_map[1] = &cb->methodB; // will return error 
+4

jeter un oeil à boost :: bind et boost :: function –

+1

@Brian: Faire une réponse pour un +1. –

+0

La seule fois où void * doit être utilisé est dans les paramètres de fonction dont le type de données est inconnu au moment de la compilation. Sinon, utilisez toujours le type de données correct même si ce type peut changer au moment de l'exécution. Vous ne pouvez rien faire avec un pointeur void * sauf (1) le passer à une autre fonction, ou (2) le transtyper dans un autre type de données. Alors pourquoi vous causer beaucoup de chagrin sur la typographie et les problèmes associés? – SjB

Répondre

1

La propre façon OO serait de définir une interface de commande. L'interface prendrait une instance (de A ou B) et tous les paramètres. Dans la méthode invoke(), il appelle la méthode de l'instance.

Vous pouvez ensuite utiliser une carte de ces interfaces de commande (définissez simplement une sous-classe commune qui définit la méthode abstraite invoke()). Le compilateur vérifierait tous les types et arguments pour vous, et vous ne devriez pas utiliser varargs.

3

Peut-être que cette information que je vous aide:

http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.1

pointeur à la méthode est de type différent de pointeur de fonction. Si vous voulez les stocker tous les deux dans une seule collection, vous devez faire des moulages manuels.

+0

Pas même les moulages manuels aider. Les pointeurs vers le membre ne peuvent pas être convertis en toute sécurité pour annuler * et revenir. Voir la section 4.11 de la norme. –

-1

Voici un exemple de code pour vous aider à démarrer. N'a pas compilé, donc pourrait nécessiter un réglage.

#define func(Instance,Method,Class) \ 
    (__int64(Instance)<<32 + __int64(&Class::Method)) 

#define invoke(Func,Method,Class) \ 
    invoke1(Func,(Class*)0)->*invoke2(Func,&Class::Method) 

template<class Class> 
Class* invoke1(__int64 Func,Class*) 
{ 
    return (Class*)(int)(Func>>32); 
} 

template<class Method> 
Method invoke2(__int64 Func,Method) 
{ 
    return (Method)(int)Func; 
} 

------------ USAGE ------------ 
class B 
{ 
    void methodB(int a,float b){} 
}; 
std::map<int, __int64> my_map; 

my_map[0] = func(cb,methodB,B); 
invoke(my_map[0],methodB,B)(1,2.f); 
+0

En plus d'être obvoisement non portable, cette implémentation s'attend également à ce que la taille du pointeur sur la méthode soit de 32 bits sur la plate-forme 32 bits. C'est absolument irréaliste. Sur la plupart des implémentations, la taille de pointeur à méthode est toujours supérieure à la taille de pointeur "ordinaire" (c'est-à-dire supérieure à 32 bits), ce qui signifie que ce qui précède ne peut tout simplement pas fonctionner. – AnT

+0

Seulement en utilisant l'héritage multiple et quelques pragmas spéciaux de compilateur j'ai réussi à obtenir des pointeurs de membre 64bit sur le système 32bit. Sur le système 64 bits, vous devez bien sûr utiliser une implémentation différente. Ce genre de choses de bas niveau nécessite toujours plusieurs implémentations pour être portable. Je préférerais avoir vingt petits en-têtes «func_xxx.h» plutôt qu'un en minuscule. – AareP

+0

Le problème est qu'il n'y a aucun moyen de savoir si une classe donnée sera utilisée dans l'environnement multi-héritage ou non (dans l'une de ses classes enfants). Pour cette raison, l'implémentation doit supposer le pire, c'est-à-dire supposer que la classe * est * utilisée avec l'héritage multiple. Par exemple, VC6 n'a pas fait cette supposition par défaut, et à la suite de ce code parfaitement légal n'a pas fonctionné correctement. – AnT

0

Vous devriez utiliser des fonctions ici. Ils peuvent être utilisés comme un remplacement flexible et de type sûr pour les pointeurs de fonction avec des signatures différentes. Une classe de base abstraite est nécessaire. Il contient l'invocation de la fonction réelle avec les paramètres communs, s'il y en a.

class Functioniod: public YourClass { 
    virtual void execute(char d, common_parameters,...) = 0 
} 

Pour chaque fonction que vous souhaitez utiliser, vous créez une classe dérivée. Le constructeur contient les paramètres spécifiques à la fonction, et la fonction execute() l'appel réel. Cette fonction d'exécution est appelée plus tard à la place du pointeur de fonction. Il doit avoir la même signature dans tous les fonctionnoïdes. Cela pourrait aussi appeler quelque chose de différent dans n'importe quelle autre classe.

class FuncA: public Functionoid { 
    FuncA(int _a, float _b, string _c, function-specific-parameters...) { 
     a = _a; b = _b; c = _c; 
    } 
    void execute(char d, common-parameters,...) { 
     call-to-member(d, a, b, c); 
    } 

    int a; 
    float b; 
    string c; 
} 

Maintenant, si vous voulez l'utiliser comme un remplacement pour votre pointeur de fonction membre, vous feriez:

std::map<int, *Functionoid> my_map; 

my_map[0] = new FuncA(someInt, someFloat, someString); 
my_map[1] = new FuncB(some-other-parameters...); 

et les exécuter avec

my_map[0]->execute(common-parm); 
my_map[1]->execute(common-parm); 
0

suite à Kamil Szot de répondre, la FAQ C++ (et le book) est une excellente référence aux profondeurs troubles de C++ et de la programmation orientée objet en général. La section 33 traite spécifiquement le problème que vous rencontrez:

En C++, les fonctions membres ont un paramètre implicite qui pointe vers l'objet (le pointeur this dans la fonction membre). Les fonctions C normales peuvent être considérées comme ayant une convention d'appel différente des fonctions membres, de sorte que les types de leurs pointeurs (pointeur vers fonction membre vs. pointeur vers fonction) sont différents et incompatibles.

Bien sûr, le answer à votre question manque quelque peu de détails.

0

Vous pouvez regarder la méthode operaters ->, :: et leurs amis. Je vais essayer de trouver un meilleur lien, mais commencez here.

MISE À JOUR: heureusement this est un meilleur article pour les pointeurs de méthodes et les opérateurs.