Me gustaría configurar un puntero de función como miembro de una clase que es un puntero a otra función en la misma clase. Las razones por las que hago esto son complicadas.
En este ejemplo, me gustaría que la salida fuera "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())
}
Pero esto falla en la compilación. ¿Por qué?
c++
oop
function-pointers
Miguel
fuente
fuente
Respuestas:
La sintaxis es incorrecta. Un puntero de miembro es una categoría de tipo diferente de un puntero ordinario. El puntero de miembro deberá usarse junto con un objeto de su clase:
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
todavía no dice en qué objeto se llamará la función. Simplemente dice que desea usar el puntero almacenado en el objetoa
. Anteponera
otro tiempo como operando izquierdo al.*
operador le dirá al compilador en qué objeto llamar a la función.fuente
(a.*a.x)()
¿Por qué(a.*x)()
no funciona?a.x
es un puntero a una función miembro de la clase A.*a.x
elimina la referencia del puntero, por lo que ahora es una referencia de función.a.(*a.x)
"enlaza" la función a una instancia (comoa.f
).(a.(*a.x))
Es necesario agrupar esta sintaxis compleja y, de(a.(*a.x))()
hecho, invoca el métodoa
sin argumentos.int (*x)()
no es un puntero a la función miembro. Un puntero a la función miembro se escribe así:int (A::*x)(void) = &A::f;
.fuente
Llamar a la función miembro en el comando de cadena
#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 atención a
(this->*func)();
la forma de declarar el puntero de función con el nombre de la clasevoid (A::*func)()
fuente
Necesita usar un puntero a una función miembro, no solo un puntero a una función.
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; }
fuente
Si bien esto se basa en las excelentes respuestas en otras partes de esta página, tuve un caso de uso que no fue completamente resuelto por ellos; para un vector de punteros a funciones, haga lo siguiente:
#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 esto es útil si está escribiendo un intérprete de comandos con funciones indexadas que deben combinarse con sintaxis de parámetros y consejos de ayuda, etc. Posiblemente también sea útil en menús.
fuente
typedef int (A::*AFunc)(int I1,int I2);
Si bien desafortunadamente no puede convertir un puntero de función miembro existente en un puntero de función simple, puede crear una plantilla de función de adaptador de una manera bastante sencilla que envuelva un puntero de función miembro conocido en tiempo de compilación en una función 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>;
Tenga en cuenta que para llamar a la función miembro, se
A
debe proporcionar una instancia de .fuente
Sobre la base de la respuesta de @ IllidanS4, he creado una clase de plantilla que permite que prácticamente cualquier función miembro con argumentos predefinidos e instancia de clase se pase por referencia para una llamada 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)...)) {} };
Prueba / ejemplo:
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, esto solo funcionará si los argumentos dados y la clase propietaria siguen siendo válidos. En cuanto a la legibilidad ... perdóneme.
Editar: eliminó el malloc innecesario haciendo que la tupla sea un almacenamiento normal. Se agregó un tipo heredado para la referencia. Se agregó la opción para proporcionar todos los argumentos en el momento de la llamada. Ahora trabajando para tener ambos ...
Edición 2: Como prometí, ambos. La única restricción (que veo) es que los argumentos predefinidos deben venir antes que los argumentos proporcionados en tiempo de ejecución en la función de devolución de llamada. Gracias a @Chipster por su ayuda con el cumplimiento de gcc. Esto funciona en gcc en ubuntu y visual studio en 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)...); }
fuente