Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: digimikeh en 2 Abril 2022, 20:56 pm



Título: alguna forma de pasar una funcion miembro como argumento a otro miembro de clase?
Publicado por: digimikeh en 2 Abril 2022, 20:56 pm
Buenas tardeS

Tengo 2 objetos:
ClaseA
ClaseB

ClaseA tiene una referencia a ClaseB en su cuerpo, pero ClaseB no debe tener referencia a ClaseA

Código
  1. //ClaseA
  2. class ClaseA {
  3.  
  4. private:
  5.    ClaseB* b {nullptr};
  6.  
  7. public:
  8.   void Alpha();
  9.  
  10. };
  11.  
  12.  
  13. //ClaseB
  14. class ClaseB {
  15. public:
  16.    void Beta();
  17. };


Lo que busco hacer es llamar desde ClaseA::Alpha() a ClaseB::Beta(), luego ClaseB::Beta() debe invocar a ClaseA::Alpha() de vuelta.


Código
  1. //ClaseA fuente
  2. void ClaseA::Alpha(){
  3.     //hacer algo
  4.     b->Beta();      //invoco a ClaseB::Beta()
  5.  
  6. }
  7.  
  8. //ClaseB fuente
  9. void ClaseB::Beta(){
  10.     //el hilo proveniente de ClaseA::Alpha pasa por aqui...
  11.     //hacer algo
  12.     //invocar de vuelta a ClaseA::Alpha() desde un principio...
  13. }


Pienso que puedo colocar algun parametro en ClaseB::Beta(), por ejemplo:

ClaseB::Beta(algun tipo o puntero a funcion miembro que apunte a la funcion anterior)

Entonces la invocacion seria:
Código
  1. b->Beta(&ClaseA::Alpha);

...cosa que al terminar Beta, ya sabe que funcion tiene que invocar despues, en este caso ClaseA::Alpha

Como se puede lograr esto?

gracias de antemano.



Título: Re: alguna forma de pasar una funcion miembro como argumento a otro miembro de clase?
Publicado por: MAFUS en 3 Abril 2022, 01:47 am
A voz de pronto se me ocurre que alpha tuviera un flag que pueda activar beta (esto para evitar una recursión infinita). Si ese flag se activa a continuará con su ejecución, sino se repetirá desde el principio. No se C++, pero en C sería algo así:
Código
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3.  
  4. bool beta() {
  5.    static int i=0;
  6.    puts("Ejecuto beta");
  7.    i++;
  8.    return i<5;
  9. }
  10.  
  11. void alpha() {
  12. void alpha() {
  13.    do {
  14.        puts("Ejecuto alpha");
  15.    } while(beta());
  16.    puts("Fin de alpha");
  17. }
  18.    puts("Fin de alpha");
  19. }
  20.  
  21. int main() {
  22.    alpha();
  23. }

Si no quieres usar el do/while puedes usar el goto (si no le tienes manía)
Código
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3.  
  4. bool beta() {
  5.    static int i=0;
  6.    puts("Ejecuto beta");
  7.    i++;
  8.    return i<5;
  9. }
  10.  
  11. void alpha() {
  12.    inicio:
  13.    puts("Ejecuto alpha");
  14.    if(beta())
  15.        goto inicio;
  16.    puts("Fin de alpha");
  17. }
  18.  
  19. int main() {
  20.    alpha();
  21. }

O mediante setjmp/longjmp (no recomendado)
Código
  1. #include <stdio.h>
  2. #include <setjmp.h>
  3.  
  4. jmp_buf buf;
  5.  
  6. void beta() {
  7.    static int i=0;
  8.    puts("Ejecuto beta");
  9.    i++;
  10.    longjmp(buf, i);
  11. }
  12.  
  13. void alpha() {
  14.    if(setjmp(buf)<5) {
  15.        puts("Ejecuto alpha");
  16.        beta();
  17.    }
  18.    puts("Fin de alpha");
  19. }
  20.  
  21. int main() {
  22.    alpha();
  23. }


Título: Re: alguna forma de pasar una funcion miembro como argumento a otro miembro de clase?
Publicado por: RayR en 3 Abril 2022, 17:35 pm
Citar
Lo que busco hacer es llamar desde ClaseA::Alpha() a ClaseB::Beta(), luego ClaseB::Beta() debe invocar a ClaseA::Alpha() de vuelta.

Esto parece un error en el diseño de tu programa. No sólo por el bucle infinito que provocará, lo cual tiene solución simple (aunque no muy limpia) sino porque ese tipo de interdependencia casi nunca es bueno. Si es curiosidad lo que tienes, sí puedes pasar punteros a funciones miembro, pero teniendo en cuenta algunos detalles.

Las funciones miembro no static, como Alpha(), siempre se ejecutan en el contexto de un objeto concreto (el puntero this, que reciben como parámetro implícito). No puedes simplemente pasar el puntero a la función, sino que necesitas saber sobre qué objeto operar. Hay varias formas de hacerlo. Una simple que podrías probar es algo así:

Declaras Beta de esta manera

Código
  1. void Beta(void (ClaseA::*funcion)(void), ClaseA& o);

La invocas desde Alpha así:

Código
  1. b->Beta(&ClaseA::Alpha, *this);

Y dentro de Beta:

Código
  1. (o.*funcion)();

No es lo más limpio, pero simplifica el código (lo de evitar el bucle infinito te lo dejo. Hay muchas maneras de hacerlo). Aún así, reitero que te recomiendo que rediseñes el programa para no necesitar esto.


Título: Re: alguna forma de pasar una funcion miembro como argumento a otro miembro de clase?
Publicado por: digimikeh en 3 Abril 2022, 18:08 pm
Gracias ambos!

El estilo C no deseo emplear mucho ya que este codigo es para continuar mi proyecto en Unreal Engine.. pero gracias de todas formas MAFUS porque me sirve de conocimiento general tu solucion.

RayR, si, efectivamente es un caso especial, estoy trabajando con corutinas, .. en este caso la clase B es una clase global, generalizada que se usa para muchos niveles, pero Clase A es una clase local que solo se utiliza para un nivel, podria haber hecho una interdependencia pero eso seria manchar ClaseB con informacion que no necesitaria mas tarde...

Lo solucione con std::function y std::bind  .. similar a lo que tu planteas.

El caso es que necesito hacerlo asi ya que es parte de una conversacion donde el hilo espera la senal del jugador (un boton) para pasar al siguiente comentario, luego con un parametro bool le digo a ClaseB que ese es el ultimo comentario para que no repita la funcion.


Saludos y gracias nuevamente.


Título: Re: alguna forma de pasar una funcion miembro como argumento a otro miembro de clase?
Publicado por: Loretz en 18 Abril 2022, 04:47 am
Hola!
Acá puedes usar el concepto de "friends" (https://isocpp.org/wiki/faq/friends)

Una cosa espantosa puede ser algo así...

Código
  1. #include <iostream>
  2.  
  3. //ClaseB
  4. class ClaseB {
  5.    friend class ClaseA;
  6. public:
  7.    void Beta(class ClaseA* a);
  8. };
  9.  
  10. //ClaseA
  11. class ClaseA {
  12. private:
  13.    ClaseB* b;
  14.    bool hecho = false;
  15.    friend class ClaseB; // https://isocpp.org/wiki/faq/friends
  16.  
  17. public:
  18.    ClaseA() : b{ new ClaseB } {}
  19.    ~ClaseA() { delete b; }
  20.    void Alpha();
  21.  
  22. };
  23.  
  24.  
  25. //ClaseA fuente
  26. void ClaseA::Alpha() {
  27.    //hacer algo
  28.    if (hecho)
  29.        return;
  30.    b->Beta(this);      //invoco a ClaseB::Beta()
  31.    std::cout << "Alpha";
  32.    hecho = true;
  33. }
  34.  
  35. //ClaseB fuente
  36. void ClaseB::Beta(class ClaseA* a) {
  37.    //el hilo proveniente de ClaseA::Alpha pasa por aqui...
  38.    //hacer algo
  39.    //invocar de vuelta a ClaseA::Alpha() desde un principio...
  40.  
  41.    if (a->hecho)
  42.        return;
  43.    a->hecho = true;
  44.    std::cout << "Beta";
  45.    a->Alpha();
  46. }
  47.  
  48. // Lo que busco hacer es llamar desde ClaseA::Alpha() a ClaseB::Beta(), luego ClaseB::Beta() debe invocar a ClaseA::Alpha() de vuelta.
  49.  
  50. int main()
  51. {
  52.    ClaseA a;
  53.    a.Alpha();
  54.  
  55. }