Gostaria de configurar um ponteiro de função como membro de uma classe que é um ponteiro para outra função na mesma classe. Os motivos pelos quais estou fazendo isso são complicados.
Neste exemplo, gostaria que a saída fosse "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())
}
Mas isso falha na compilação. Por quê?
c++
oop
function-pointers
Mike
fonte
fonte
Respostas:
A sintaxe está errada. Um ponteiro de membro é uma categoria de tipo diferente de um ponteiro comum. O ponteiro do membro deverá ser usado junto com um objeto de sua 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
ainda não diz em qual objeto a função deve ser chamada. Apenas diz que você deseja usar o ponteiro armazenado no objetoa
. Anexara
outro tempo como o operando à esquerda para o.*
operador informará ao compilador em qual objeto chamar a função.fonte
(a.*a.x)()
.Por que(a.*x)()
não funciona?a.x
é um ponteiro para uma função-membro da classe A.*a.x
desreferencia o ponteiro, então agora é uma referência de função.a.(*a.x)
"vincula" a função a uma instância (exatamente comoa.f
).(a.(*a.x))
é necessário agrupar essa sintaxe complexa e(a.(*a.x))()
realmente invocar o métodoa
sem argumentos.int (*x)()
não é um ponteiro para a função de membro. Um ponteiro para função de membro é escrito assim:int (A::*x)(void) = &A::f;
.fonte
Chamar função membro no comando de string
#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; }
Preste atenção
(this->*func)();
e a maneira de declarar o ponteiro de função com o nome da classevoid (A::*func)()
fonte
Você precisa usar um ponteiro para uma função de membro, não apenas um ponteiro para uma função.
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; }
fonte
Embora isso seja baseado nas excelentes respostas em outras partes desta página, eu tive um caso de uso que não foi completamente resolvido por elas; para um vetor de ponteiros para funções, faça o seguinte:
#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; }
Algo como isso é útil se você estiver escrevendo um interpretador de comandos com funções indexadas que precisam ser combinadas com a sintaxe de parâmetro e dicas de ajuda, etc. Possivelmente também útil em menus.
fonte
typedef int (A::*AFunc)(int I1,int I2);
Embora você infelizmente não possa converter um ponteiro de função de membro existente em um ponteiro de função simples, você pode criar um modelo de função de adaptador de uma maneira bastante direta que envolve um ponteiro de função de membro conhecido em tempo de compilação em uma função normal como esta:
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>;
Observe que, para chamar a função de membro, uma instância de
A
deve ser fornecida.fonte
Com base na resposta de @ IllidanS4, criei uma classe de modelo que permite que praticamente qualquer função de membro com argumentos predefinidos e instância de classe seja passada por referência para chamada posterior.
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)...)) {} };
Teste / exemplo:
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 }
Obviamente, isso só funcionará se os argumentos fornecidos e a classe do proprietário ainda forem válidos. Quanto à legibilidade ... por favor, me perdoe.
Edit: removeu o malloc desnecessário, tornando o armazenamento normal da tupla. Adicionado tipo herdado para a referência. Adicionada opção para fornecer todos os argumentos no momento da chamada. Agora trabalhando para ter os dois ...
Editar 2: Como prometido, ambos. A única restrição (que vejo) é que os argumentos predefinidos devem vir antes dos argumentos fornecidos pelo tempo de execução na função de retorno de chamada. Obrigado a @Chipster por alguma ajuda com conformidade com o gcc. Isso funciona no gcc no ubuntu e no visual studio no 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)...); }
fonte