Je voudrais configurer un pointeur de fonction en tant que membre d'une classe qui est un pointeur vers une autre fonction de la même classe. Les raisons pour lesquelles je fais cela sont compliquées.
Dans cet exemple, je voudrais que la sortie soit "1"
class A {
public:
int f();
int (*x)();
}
int A::f() {
return 1;
}
int main() {
A a;
a.x = a.f;
printf("%d\n",a.x())
}
Mais cela échoue à la compilation. Pourquoi?
c++
oop
function-pointers
Mike
la source
la source
Réponses:
La syntaxe est erronée. Un pointeur de membre est une catégorie de type différente d'un pointeur ordinaire. Le pointeur de membre devra être utilisé avec un objet de sa classe:
class A { public: int f(); int (A::*x)(); // <- declare by saying what class it is a pointer to }; int A::f() { return 1; } int main() { A a; a.x = &A::f; // use the :: syntax printf("%d\n",(a.*(a.x))()); // use together with an object of its class }
a.x
ne dit pas encore sur quel objet la fonction doit être appelée. Il indique simplement que vous souhaitez utiliser le pointeur stocké dans l'objeta
. La pré-attentea
une autre fois en tant qu'opérande de gauche à l'.*
opérateur indiquera au compilateur sur quel objet appeler la fonction.la source
(a.*a.x)()
.Pourquoi ne(a.*x)()
fonctionne pas?a.x
est un pointeur vers une fonction membre de la classe A.*a.x
déréférence le pointeur donc maintenant c'est une référence de fonction.a.(*a.x)
"lie" la fonction à une instance (tout commea.f
).(a.(*a.x))
est nécessaire pour regrouper cette syntaxe complexe et(a.(*a.x))()
invoque en fait la méthodea
sans argument.int (*x)()
n'est pas un pointeur vers une fonction membre. Un pointeur vers une fonction membre est écrit comme ceci:int (A::*x)(void) = &A::f;
.la source
Appel d'une fonction membre sur une commande de chaîne
#include <iostream> #include <string> class A { public: void call(); private: void printH(); void command(std::string a, std::string b, void (A::*func)()); }; void A::printH() { std::cout<< "H\n"; } void A::call() { command("a","a", &A::printH); } void A::command(std::string a, std::string b, void (A::*func)()) { if(a == b) { (this->*func)(); } } int main() { A a; a.call(); return 0; }
Faites attention à
(this->*func)();
et à la manière de déclarer le pointeur de fonction avec le nom de la classevoid (A::*func)()
la source
Vous devez utiliser un pointeur vers une fonction membre, pas seulement un pointeur vers une fonction.
class A { int f() { return 1; } public: int (A::*x)(); A() : x(&A::f) {} }; int main() { A a; std::cout << (a.*a.x)(); return 0; }
la source
Bien que cela soit basé sur les réponses solides ailleurs sur cette page, j'avais un cas d'utilisation qui n'a pas été complètement résolu par eux; pour un vecteur de pointeurs vers des fonctions, procédez comme suit:
#include <iostream> #include <vector> #include <stdio.h> #include <stdlib.h> class A{ public: typedef vector<int> (A::*AFunc)(int I1,int I2); vector<AFunc> FuncList; inline int Subtract(int I1,int I2){return I1-I2;}; inline int Add(int I1,int I2){return I1+I2;}; ... void Populate(); void ExecuteAll(); }; void A::Populate(){ FuncList.push_back(&A::Subtract); FuncList.push_back(&A::Add); ... } void A::ExecuteAll(){ int In1=1,In2=2,Out=0; for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){ Out=(this->*FuncList[FuncId])(In1,In2); printf("Function %ld output %d\n",FuncId,Out); } } int main(){ A Demo; Demo.Populate(); Demo.ExecuteAll(); return 0; }
Quelque chose comme ceci est utile si vous écrivez un interpréteur de commandes avec des fonctions indexées qui doivent être associées à la syntaxe des paramètres et des conseils d'aide, etc. Peut-être aussi utile dans les menus.
la source
typedef int (A::*AFunc)(int I1,int I2);
Bien que vous ne puissiez malheureusement pas convertir un pointeur de fonction membre existant en un pointeur de fonction simple, vous pouvez créer un modèle de fonction adaptateur d'une manière assez simple qui encapsule un pointeur de fonction membre connu au moment de la compilation dans une fonction normale comme celle-ci:
template <class Type> struct member_function; template <class Type, class Ret, class... Args> struct member_function<Ret(Type::*)(Args...)> { template <Ret(Type::*Func)(Args...)> static Ret adapter(Type &obj, Args&&... args) { return (obj.*Func)(std::forward<Args>(args)...); } }; template <class Type, class Ret, class... Args> struct member_function<Ret(Type::*)(Args...) const> { template <Ret(Type::*Func)(Args...) const> static Ret adapter(const Type &obj, Args&&... args) { return (obj.*Func)(std::forward<Args>(args)...); } };
int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;
Notez que pour appeler la fonction membre, une instance de
A
doit être fournie.la source
En m'appuyant sur la réponse de @ IllidanS4, j'ai créé une classe modèle qui permet à pratiquement n'importe quelle fonction membre avec des arguments prédéfinis et une instance de classe d'être passée par référence pour un appel ultérieur.
template<class RET, class... RArgs> class Callback_t { public: virtual RET call(RArgs&&... rargs) = 0; //virtual RET call() = 0; }; template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> { public: T * owner; RET(T::*x)(RArgs...); RET call(RArgs&&... rargs) { return (*owner.*(x))(std::forward<RArgs>(rargs)...); }; CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {} }; template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> { public: T* owner; RET(T::*x)(Args...); RET call() { return (*owner.*(x))(std::get<Args&&>(args)...); }; std::tuple<Args&&...> args; CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x), args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {} };
Test / exemple:
class container { public: static void printFrom(container* c) { c->print(); }; container(int data) : data(data) {}; ~container() {}; void print() { printf("%d\n", data); }; void printTo(FILE* f) { fprintf(f, "%d\n", data); }; void printWith(int arg) { printf("%d:%d\n", data, arg); }; private: int data; }; int main() { container c1(1), c2(20); CallbackCreattimeArgs<container, void> f1(&c1, &container::print); Callback_t<void>* fp1 = &f1; fp1->call();//1 CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout); Callback_t<void>* fp2 = &f2; fp2->call();//20 CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith); Callback_t<void, int>* fp3 = &f3; fp3->call(15);//20:15 }
Évidemment, cela ne fonctionnera que si les arguments donnés et la classe propriétaire sont toujours valides. En ce qui concerne la lisibilité ... veuillez me pardonner.
Éditer: supprimé malloc inutile en faisant le stockage normal du tuple. Ajout d'un type hérité pour la référence. Ajout d'une option pour fournir tous les arguments au moment de l'appel à la place. Je travaille maintenant à avoir les deux ...
Edit 2: Comme promis, les deux. La seule restriction (que je vois) est que les arguments prédéfinis doivent venir avant les arguments fournis par l'exécution dans la fonction de rappel. Merci à @Chipster pour son aide sur la conformité gcc. Cela fonctionne sur gcc sur ubuntu et Visual Studio sur Windows.
#ifdef _WIN32 #define wintypename typename #else #define wintypename #endif template<class RET, class... RArgs> class Callback_t { public: virtual RET call(RArgs... rargs) = 0; virtual ~Callback_t() = default; }; template<class RET, class... RArgs> class CallbackFactory { private: template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> { private: T * owner; RET(T::*x)(CArgs..., RArgs...); std::tuple<CArgs...> cargs; RET call(RArgs... rargs) { return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...); }; public: Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda); ~Callback() {}; }; public: template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)); }; template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {} template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) { return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...); }
la source