Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Punteros a plantillas de funciones...¿posible?

Estas en el tema de Punteros a plantillas de funciones...¿posible? en el foro de C/C++ en Foros del Web. Bueno, sigo con otro capítulo de mi culebrón: Cap 1 Cap 2 Una vez aprendido a manejarme con punteros a funciones, punteros a métodos de ...
  #1 (permalink)  
Antiguo 06/02/2014, 04:22
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 6 meses
Puntos: 10
Punteros a plantillas de funciones...¿posible?

Bueno, sigo con otro capítulo de mi culebrón:

Cap 1
Cap 2

Una vez aprendido a manejarme con punteros a funciones, punteros a métodos de clase y punteros a métodos de instancias, sigo sin tener claro cómo hacer uso de ellos de forma flexible y conseguir que me sirvan para varias cosas diferentes.

Me explico:

parto de esté método de una clase que representa a un grafo:
Código C++:
Ver original
  1. template <typename datonodo_t, typename datoarista_t>
  2. void Grafo<datonodo_t,datoarista_t>::recorrerHijos(pNodo& padre, void (*pfunc)(pArista& a))
  3. {
  4.     if (padre->adyacente!=0)
  5.         {
  6.             pArista A=padre->adyacente;
  7.              while (A->siguiente!=0)
  8.                 {
  9.                     pfunc(A);
  10.                     A=A->siguiente;
  11.                 }
  12.             pfunc(A);
  13.         }
  14. }

Haciendo uso de una función miembro estática

Código C++:
Ver original
  1. static void imprimir(arista<datoarista,datonodo>*& a)
  2. {
  3.      cout<<a->dato;
  4. }

La puedo invocar desde otro método de la misma clase
Código C++:
Ver original
  1. void Manejador::MostrarHijos()
  2. {
  3.    G.recorrerHijos(padre, imprimir);
  4.  }


Pero a este sistema le veo una limitación (aparte de mi ) y es que estoy obligado a crear todas las funciones con la firma:
Código C++:
Ver original
  1. (void)(pfunc*)(arista<datoarista,datonodo>*& a)

Supongamos que ahora quiero usar la misma función que recorre los hijos directos de un nodo, pero para comparar sus valores.....pues ya no me sirve esa forma, o al menos no encuentro la manera de hacerlo.

Entonces se me ocurre que tal vez, si la firma de la función fuera una plantilla, algo como:
Código C++:
Ver original
  1. template <typename T, typename D>
  2. (T)(pfunc*)(D)
me podría ofrecer algo más de flexibilidad a la hora de su uso.
Pero no se me ocurre cómo implementarlo.

Por otro lado, he leído algo sobre functores, pero igualmente no sé como podría implementarlo en mi caso.

En fin, que como siempre, cualquier ayuda será agradecida
  #2 (permalink)  
Antiguo 06/02/2014, 05:33
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 6 meses
Puntos: 10
Respuesta: Punteros a plantillas de funciones...¿posible?

Bueno, pues al menos poder, parece que se puede....pero no sé cómo aplicarlo en mi caso.
Código C++:
Ver original
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template <typename T>
  5. T maximo (T a, T b)
  6. {
  7.     if (a>b)
  8.     {
  9.         return a;
  10.     }
  11.     return b;
  12. }
  13.  
  14. template <typename TT>
  15. struct functor
  16. {
  17.     TT _a;
  18.     TT _b;
  19.     functor (TT a=0, TT b=0): _a(a),_b(b){}
  20.     TT operator()()
  21.     {
  22.         if (_a>_b)
  23.         {
  24.             return _a;
  25.         }
  26.         return _b;
  27.     }
  28. };
  29.  
  30. int main()
  31. {
  32.  
  33.     int(*maxint)(int a,int b)=&maximo;
  34.     float(*maxfloat)(float a, float b)=&maximo;
  35.  
  36.     cout<<maxint(800,80)<<endl;
  37.     cout<<maxfloat (6.75,6.755)<<endl;
  38.  
  39.     functor<int>i(4,5);
  40.     cout<<i()<<endl;
  41.  
  42.     functor<float>f(4.5,3.2);
  43.     cout<<f()<<endl;
  44.  
  45.     return 0;
  46. }

A ver si alguien me echa una mano...
  #3 (permalink)  
Antiguo 06/02/2014, 06:24
Avatar de L3m0n  
Fecha de Ingreso: diciembre-2011
Mensajes: 219
Antigüedad: 12 años, 3 meses
Puntos: 46
Respuesta: Punteros a plantillas de funciones...¿posible?

Buenas, supongo que con C++ habrá alguna manera mejor de hacer lo que voy a decir, sino será cuestión de investigar. Yo te voy a decir lo que haría yo en C:

La cosa es poner todos tus punteros a funciones en una union. Imagina que tienes dos funciones muy tontas: suma y doble. Pues harías algo así:
Código C:
Ver original
  1. typedef union
  2. {
  3.     int (*suma)(int a, int b);
  4.     int (*doble)(int a);
  5. }funciones;
Ahora, creas una union en el main o en la funcion que estés y le asignas la funcion que vayas a usar:
Código C:
Ver original
  1. funciones func;
  2.  
  3. if("quieres sumar")
  4.     func.suma = suma; //supon que tienes la funcion suma ya hecha
  5. else if("quieres usar el doble")
  6.     func.doble = doble;
Y ahora tienes que definir la funcion a la que mandas esto para que reciba una union:
Código C:
Ver original
  1. int funcion(funciones f);


O directamente puedes hacer la funcion para que segun una condicion que tu pases haga una cosa o otra:
Código C:
Ver original
  1. enum {SUMA = 1, DOBLE = 2};
  2.  
  3. int funcion(funciones f, int condicion)
  4. {
  5.      if(condicion == SUMA)
  6.            f.suma = suma;
  7.      else if(condicion == DOBLE)
  8.            f.doble = doble;
  9.  
  10.      // Y ahora llamas a la funcion:
  11.      return f.suma(2,3);
  12. }

Para finalizar te dire que hay una cosa bastante divertida a la que se le puede sacar bastante utilidad y es que aunque tu llames a la funcion f.suma, en realidad siempre se usara la ultima funcion que hayas asignado, así que si yo pongo f.suma pero la ultima que he asingnado ha sido doble, me usara la funcion doble.

Y hay una cosa aún mas "divertida", y es que aunque llames a la funcion
Código C:
Ver original
  1. f.suma(2,3);
Si tu lo ultimo que has definido ha sido doble, hara doble y lo bueno es que sin ningun problema, simplemente usara el primer argumento y del otro se olvidará, sin dar ningún problema.


Espero haber ayudado, aunque no sea C++, siempre sirve de algo xD
  #4 (permalink)  
Antiguo 06/02/2014, 08:05
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 15 años, 10 meses
Puntos: 61
Respuesta: Punteros a plantillas de funciones...¿posible?

La explicacion parece un trabalenguas:

El desvío usando functors permite pasar 1 o mas argumentos a una funcion, pero esto en realidad no se hace al invocar la funcion, sino que se pasan al crear el objeto functor. Si este objeto provee del operador () las funciones que reciben y usan el functor la llaman para obtener el resultado.
En resumen lo que se hace es desplazar la responsabilidad de QUE HACER en el functor, dejando la responsabilidad de CUANDO HACERLO en la funcion que lo recibe.

Mirar, en el ejemplo, la funcion imprimeResultado(), recibe un tipo generico, se usa con 2 functors distintos

Los functors
Código C++:
Ver original
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template <typename T>
  5. class FMenor {
  6.    T _a;
  7.    T _b;
  8. public:
  9.    FMenor ( const T& a, const T& b ) : _a(a), _b(b) {}
  10.    T operator()() {
  11.      if( _a < _b ) { return _a; }
  12.      return _b;
  13.    }
  14. };
  15.  
  16. template <typename T>
  17. class Fagrega1 {
  18.    T _a;
  19. public:
  20.    Fagrega1 ( const T& a ) : _a(a) {}
  21.    T operator()() {
  22.      return _a+1;
  23.    }
  24. };

La funcion
Código C++:
Ver original
  1. template <typename T>
  2. void imprimeResultado( T f ) {
  3.   cout << f() << endl;
  4. }

El uso
Código C++:
Ver original
  1. int main()
  2. {
  3.    Fagrega1<int> fa(2);
  4.    FMenor<int> fm( 30, 4 );
  5.  
  6.    imprimeResultado( fa );
  7.    imprimeResultado( fm );
  8.  
  9.    return 0;
  10. }

Mirar funcionando aqui: http://goo.gl/qqkXPt
__________________
Visita mi perfil en LinkedIn

Última edición por CalgaryCorpus; 06/02/2014 a las 08:13
  #5 (permalink)  
Antiguo 11/02/2014, 05:18
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 6 meses
Puntos: 10
Respuesta: Punteros a plantillas de funciones...¿posible?

Gracias L3m0n por la interesante respuesta. La verdad es que a veces me parece que las uniones son las grandes desaprovechadas...están ahí y se usan poco. Me ha gustado mucho el enfoque que le has dado para guardar la lista de funciones a usar.

Pero al final he usado el "invento" de los functores.
La verdad es que me proporciona la ventaja del polimorfismo de C++.
Así, por ahora (y espero que me dure) lo he he hecho de ésta forma:
Código C++:
Ver original
  1. template <typename datonodo_t, typename datoarista_t>
  2. void Grafo<datonodo_t,datoarista_t>::recorrerHijos(const pNodo& padre, Tratar& tratamiento)
  3. {
  4.     if (padre->adyacente!=0)
  5.         {
  6.             pArista A=padre->adyacente;
  7.              while (A->siguiente!=0)
  8.                 {
  9.                     tratamiento(A);
  10.                     A=A->siguiente;
  11.                 }
  12.             tratamiento(A);
  13.         }
  14. }

en lugar del puntero a función le paso un objeto de la clase Tratar

y luego la clase Tratar es abstracta, y voy definiendo "la perrería" que le quiero hacer en cada clase derivada.

Código C++:
Ver original
  1. struct Tratar;
y de ahí derivo:
Código C++:
Ver original
  1. struct Buscar:public Tratar
ó
Código C++:
Ver original
  1. struct Imprimir:public Tratar

Y por supuesto, en las funciones miembro de la clase que maneja al grafo, tengo:

Código C++:
Ver original
  1. Tratar* tratamiento= new Buscar (nombreNodo, auxiliar);
ó
Código C++:
Ver original
  1. Tratar* tratamiento=new Imprimir (nombreNodo, auxiliar);

en cada función miembro.

Bueno, marco el tema como solucionado y me compro una botella de vino chileno para celebrarlo
  #6 (permalink)  
Antiguo 12/02/2014, 08:56
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 6 meses
Puntos: 10
Respuesta: Punteros a plantillas de funciones...¿posible?

Hola de nuevo:

Complemento un poco la respuesta por si a alguien le puede venir bien saber cómo ha quedado resuelto mi problema.

Parto de que tengo una clase grafo, que entre otros métodos tiene uno que muestra los hijos de un nodo dado:

Código C++:
Ver original
  1. class TratarArista;//declaración previa Importante!!
  2.  
  3.  
  4. template <typename datonodo_t, typename datoarista_t>
  5. void Grafo<datonodo_t,datoarista_t>::recorrerHijos(const pNodo& padre, TratarArista& tratamiento)
  6. {
  7.     if (padre->adyacente!=0)
  8.         {
  9.             pArista A=padre->adyacente;
  10.             while (A->siguiente!=0)
  11.                 {
  12.                     tratamiento(A);
  13.                     A=A->siguiente;
  14.                 }
  15.             tratamiento(A);
  16.         }
  17. }

Esta función me puede venir bien para mostrar los nodos de un hijo, o buscar algún valor entre ellos, etc.

Entonces, por sugerencia de CalgaryCorpus, he usado functores.

Los he definido así (algo ha cambiado desde el post anterior):

Pongo el ejemplo de Imprimir

(Todos los functores los he metido en un fichero Functores.h)


Código C++:
Ver original
  1. struct TratarArista
  2. {
  3.     //miembros:
  4.     string Nombre;
  5.     arista<Medicion,Concepto>*& AA;
  6.  
  7.     //constructor:
  8.     TratarArista(string nombre, arista<Medicion,Concepto>*& aux);
  9.  
  10.     //función miembro:
  11.     virtual void operator()(arista<Medicion,Concepto>* a)=0;
  12. };
  13.  
  14. struct Imprimir:public TratarArista
  15. {
  16.     Imprimir (string nombre, arista<Medicion,Concepto>*& aux);
  17.  
  18.     virtual void operator()(arista<Medicion,Concepto>* a)
  19.     {
  20.         cout<<"│"<<a->destino->datonodo.LeeCodigo()<<"│"<<a->destino->datonodo.LeeUd()<<a->destino->datonodo.LeeResumen()<<"│"<<endl;        
  21.     }
  22. };

Ahora sólo me queda llamarlos dentro de la clase que contiene al grafo.
Por ejemplo:
Código C++:
Ver original
  1. void Contenedor::MostrarHijos()
  2. {
  3.     string nombreNodo="";
  4.     TratarArista* tratamiento=new Imprimir (nombreNodo, auxiliar);    
  5.     G.recorrerHijos(padre, *tratamiento);
  6. }

(En este caso no me interesa el valor del string, por eso entro con una cadena vacía. Hay otros casos en los que quiero buscar el Codigo de un nodo, y entonces sí me interesa entrar con el string teniendo ese valor, y lo que hará la función entonces es comparar y en caso de encontrar el código asignar la arista a auxiliar, que es una arista que tiene la clase contenedora y que sirve para estos temas auxiliares)

En otro momento puedo necesitar imprimir los nodos, pero no los hijos directos de un nodo, sino toda la rama que sale de un nodo (aclaro que el objetivo es tener un grafo acíclico, cuyo desarrollo desde cualquier nodo es un árbol...no sé si lo he dicho bien):

Código C++:
Ver original
  1. template <typename datonodo_t, typename datoarista_t>
  2. void Grafo<datonodo_t,datoarista_t>::recorrerGrafo(pNodo& inicio, TratarArista& tratamiento)
  3. {
  4.     pArista A;
  5.     if (inicio)
  6.         {
  7.             guardaAristas (inicio);//meto las aristas en la pila
  8.             while (!pila.empty())
  9.                 {
  10.                     A=pila.top();
  11.                     pila.pop();
  12.                     tratamiento(A);
  13.                     recorrerGrafo(A->destino,tratamiento);
  14.                 }
  15.         }
  16. }

Sólo he de añadir al método que recorre el grafo de ésta forma otro objeto de la clase TratarArista, y en caso de querer imprimir actúo como en el caso anterior:
Código C++:
Ver original
  1. void Contenedor::VerArbol()
  2. {
  3.     auxiliar=padre->adyacente;
  4.     TratarArista* tratamiento=new Imprimir ("", auxiliar);
  5.     G.recorrerGrafo(padre, *tratamiento);
  6. }
Bueno, me explico regular, pero dicho queda

Última edición por dehm; 12/02/2014 a las 09:06

Etiquetas: funcion, metodo, plantillas, punteros
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 05:51.