Puntero de función a función miembro

12 minutos de lectura

Puntero de funcion a funcion miembro
Miguel

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 estoy haciendo 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é?

  • Consulte también Llamar a métodos de clase de C++ a través de un puntero de función.

    – jww

    2 de agosto de 2019 a las 4:54

  • @jww y verifique la respuesta de CiroSantilli en esa pregunta, otras respuestas están más o menos fuera de tema. Básicamente, simplemente int (C::*function_pointer_var)(int) = &C::method; luego Cc; y (c.*function_pointer_var)(2).

    – jw_

    31 de enero de 2020 a las 3:15


Puntero de funcion a funcion miembro
Johannes Schaub – litb

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 sobre qué objeto se va a llamar la función. Simplemente dice que desea usar el puntero almacenado en el objeto a. anteponiendo a otra vez como el operando izquierdo del .* El operador le dirá al compilador sobre qué objeto llamar a la función.

  • Sé que esto es viejo, pero no entiendo el uso de (a.*a.x)().Por que (a.*x)() ¿no trabajo?

    – Gaurav Sehgal

    28 de junio de 2016 a las 5:19

  • @gau porque x no está en el alcance

    – Johannes Schaub – litb

    28 de junio de 2016 a las 14:12

  • Tengo que buscar esto cada vez que lo uso también. La sintaxis es confusa, pero tiene sentido si la desglosas. 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) “vincula” la función a una instancia (al igual que a.f). (a.(*a.x)) es necesario agrupar esta compleja sintaxis, y (a.(*a.x))() en realidad invoca el método en a sin argumentos.

    – jwm

    06/04/2018 a las 20:41

int (*x)() no es un puntero a la función miembro. Una función puntero a miembro se escribe así: int (A::*x)(void) = &A::f;.

1647587168 871 Puntero de funcion a funcion miembro
Él para

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;
}

presta atención a (this->*func)(); y la forma de declarar el puntero de función con nombre de clase void (A::*func)()

Debe 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;
}

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, una instancia de A debe ser provisto.

  • Me has inspirado, @IllidanS4. Mira mi respuesta. +1

    – memtha

    3 de octubre de 2019 a las 5:57

Si bien esto se basa en las excelentes respuestas en otras partes de esta página, tuve un caso de uso que no resolvieron por completo; 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 la sintaxis de parámetros y consejos de ayuda, etc. Posiblemente también sea útil en los menús.

  • Me has inspirado, @IllidanS4. Mira mi respuesta. +1

    – memtha

    3 de octubre de 2019 a las 5:57

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 instancias de clase se pasen por referencia para llamadas posteriores.



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
};

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
        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 proporcionados y la clase propietaria siguen siendo válidos. En cuanto a la legibilidad… por favor, perdóname.

Editar: Se eliminó el malloc innecesario al hacer que la tupla tuviera un almacenamiento normal. Se agregó un tipo heredado para la referencia. Opción agregada para proporcionar todos los argumentos en el momento de la llamada. Ahora trabajando en tener ambos…

Edición 2: Como prometí, ambos. La única restricción (que veo) es que los argumentos predefinidos deben venir antes de 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
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)...);
}

Editar 3: Cumplimiento de normas, mayor flexibilidad y ejemplos. (Extraído de mi proyecto de pasatiempo activo, que planeo abrir como fuente… eventualmente).

//CallbackFactory.h
#pragma once

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif

namespace WITE {

  template<class RET, class... RArgs> class Callback_t {
  public:
    virtual RET call(RArgs... rargs) const = 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:
      RET(T::*x)(CArgs..., RArgs...);
      T * owner;
      std::tuple<CArgs...> cargs;
    public:
      Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
      ~Callback() {};
      RET call(RArgs... rargs) const override {
        return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
      };
    };
    template<class... CArgs> class StaticCallback : public Callback_t<RET, RArgs...> {
    private:
      RET(*x)(CArgs..., RArgs...);
      std::tuple<CArgs...> cargs;
    public:
      StaticCallback(RET(*x)(CArgs..., RArgs...), CArgs... pda);
      ~StaticCallback() {};
      RET call(RArgs... rargs) const override {
        return (*x)(std::get<CArgs>(cargs)..., rargs...);
      };
    };
  public:
    typedef Callback_t<RET, RArgs...>* callback_t;
    template<class U, class... CArgs> static callback_t make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
    template<class... CArgs> static callback_t make(CArgs... cargs, RET(*func)(CArgs..., RArgs...));//for non-members or static members
  };
  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

  template<class RET2, class... RArgs2> template<class... CArgs2>
  CallbackFactory<RET2, RArgs2...>::StaticCallback<CArgs2...>::StaticCallback(RET2(*x)(CArgs2..., RArgs2...), CArgs2... pda) :
    x(x), 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)...);
  };

  template<class RET, class... RArgs> template<class... CArgs> Callback_t<RET, RArgs...>*
  CallbackFactory<RET, RArgs...>::make(CArgs... cargs, RET(*func)(CArgs..., RArgs...)) {
    return new wintypename CallbackFactory<RET, RArgs...>::StaticCallback<CArgs...>(func, std::forward<CArgs>(cargs)...);
  };

  #define typedefCB(name, ...) typedef WITE::CallbackFactory<__VA_ARGS__> name## _F; typedef typename name## _F::callback_t name ;

  typedefCB(rawDataSource, int, void*, size_t)

};

//example:
class Integer {
public:
  typedefCB(oneInOneOut, int, int);
  typedefCB(twoInOneOut, int, int, int);
  int value;
  Integer(int v) : value(v) {};
  int plus(int o) {
    return value + o;
  };
  int plus(int a, int b, int c) {
    return value + a + b + c;
  };
  static int simpleSum(int a, int b) {
    return a + b;
  };
};

int main(int argc, char** argv) {
  Integer::twoInOneOut sumOfTwo = Integer::twoInOneOut_F::make(&Integer::simpleSum);
  std::cout << sumOfTwo->call(5, 6) << std::endl;//11
  //
  Integer seven(7);
  Integer::oneInOneOut sevenPlus = Integer::oneInOneOut_F::make<Integer>(&seven, &Integer::plus);
  std::cout << sevenPlus->call(12) << std::endl;//19
  //
  Integer::twoInOneOut seventeenPlus = Integer::twoInOneOut_F::make<Integer, int>(&seven, 10, &Integer::plus);//provide the 1st arg here, and the other 2 when called
  std::cout << seventeenPlus->call(52, 48) << std::endl;//117
}



Mientras escribía esto, me encontré con libstdc++ error conocido #71096 que se rompe std::get cuando se proporciona >1 argumento en el momento de la construcción de la devolución de llamada. Este error se ha marcado como solución en gcc 11, que lamentablemente no se ha incluido en el repositorio de ubuntu en este momento (apt dice que estoy actualizado con 9.3.0)

  • ¡Buen trabajo! Lo aprecio. Pero, ¿podría dar un breve ejemplo del ejemplo de uso para Edit 2?

    – BenHéroe

    28 de septiembre de 2021 a las 14:39


  • @BenHero ver edición 3

    – memtha

    28 de septiembre de 2021 a las 23:29

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad