Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Llamar a método virtual de un objeto pasado como parámetro (https://www.clubdelphi.com/foros/showthread.php?t=33575)

Ñuño Martínez 11-07-2006 13:30:50

Llamar a método virtual de un objeto pasado como parámetro
 
He estado buscando en el foro y en Internet pero no he encontrado nada.

Tengo un programa que, simplificando mucho, es el siguiente. (No creo que a nadie le cueste seguirlo)

Código Delphi [-]
PROGRAM base;

TYPE
  CLASE_BASE = CLASS
  PUBLIC
    PROCEDURE Metodo; VIRTUAL;
  END;

  CLASE_HIJA = CLASS (CLASE_BASE)
  PUBLIC
    PROCEDURE Metodo; OVERRIDE;
  END;

  PROCEDURE CLASE_BASE.Metodo;
  BEGIN
    WriteLn ('Llamó a CLASE_BASE');
  END;

  PROCEDURE CLASE_HIJA.Metodo;
  BEGIN
    WriteLn ('Llamó a CLASE_HIJA');
  END;

(* Aquí está el problema.  Es un procedimiento que se va a usar como
   "callback", de ahí que reciba un POINTER. *)
PROCEDURE Procedimiento (Objeto: POINTER);
BEGIN
  WITH CLASE_BASE (Objeto) DO
    Metodo;
END;

VAR
  Objeto: CLASE_BASE;
BEGIN
(* Aquí ponemos en marcha todo. *)
  Objeto := CLASE_HIJA.Create;
{ Simulamos una llamada "callback" }
  Procedimiento ( POINTER (Objeto));
  Objeto.Free;
END.

Como dice el comentario, voy a utilizar un procedimiento como "callback", es decir, que voy a pasar la dirección de dicho procedimiento a una librería ya escrita para que lo llame cuando sea necesario (parecido a la función de procesamiento de mensajes de ventana en Windows).

El problema es que cuando se llama al Procedimiento siempre llama al método de la CLASE_BASE y nunca al de la CLASE_HIJA. ¿Por qué me sucede esto? ¿Cómo puedo hacer que llame al método apropiado?

Ñuño Martínez 11-07-2006 16:53:22

1 Archivos Adjunto(s)
Acabo de comprobar que el ejemplo que he puesto anteriormente no reproduce el error, así que voy a incluir el código completo (no es mucho, pero necesitarán conocer la libería GTK+ y tenerla instalada, por eso no quise adjuntar el código desde el principio). También creo que sé de dónde puede venir el error: del uso de SELF al definir la "señal" dentro de TGTK_OBJECT, pero no se me ocurre otra forma de hacerlo (bueno, sí, utilizando punteros a "PROCEDURE OF CLASS" en campos "On<Evento>" al estilo de la VCL, pero no me parece una forma elegante de enfrentarse al problema).

Una solución sería que alguien conociera una buena envolvente en Objetive Pascal de la librería GTK+, pero las únicas que he encontrado o no me compilan o no siguen el diseño original de dicha librería, a parte de que estaría más orgulloso de mí mismo si la consiguiera hacer yo, claro... ;)

luisgutierrezb 11-07-2006 17:34:51

y si usas Inherited nombre_metodo ¿?

Ñuño Martínez 11-07-2006 17:51:53

No, porque quien hace la llamada es un procedimiento que nada tiene que ver con las clases (recibe el objeto como parámetro).

Creo que, después de pensarlo, he encontrado un punto de partida. El siguiente método es el de la clase "base":
Código Delphi [-]
(* TGTK_OBJECT::signal_connect:
 *   Conecta la señal con el procedimiento o función de respuesta
 *   correspondiente. *)
PROCEDURE TGTK_OBJECT.signal_connect (aName: Pgchar; aFunc: TGtkSignalFunc);
BEGIN
  gtk_signal_connect (SELF.GtkObject, aName, aFunc, SELF);
END;

Lo que hace es registrar un procedimiento (pasado en el parámetro aFunc) para que sea utilizado como respuesta al evento 'aName'.

El procedimiento 'gtk_signal_connect' recibe, primero, el puntero al objeto GTK (algo así como el handle de un control Windows), el nombre del evento, el puntero de la función o procedimiento de respuesta del evento (no puede estar dentro de una clase) y un valor que será pasado a la función de respuestas al evento. Yo le paso la referencia al objeto para recuperarlo más tarde. Así, para el evento "destroy" utilizo la siguiente llamada:

Código Delphi [-]
(*** Señales "signals" ***)

(* _signal_destroy_:
 *   Respuesta a la señal "destroy". *)
PROCEDURE _signal_destroy_ (ObjetoGTK: pGtkObject; aData: TGTK_OBJECT); CDECL;
BEGIN
  aData.sDestroy;
END;



(* TGTK_OBJECT::Create:
 *   Crea el objeto, asignando su puntero. *)
CONSTRUCTOR TGTK_OBJECT.Create (aGtkObject: PGtkObject);
BEGIN
  fGtkObject := aGtkObject;
{ Respuesta a las señales. }
WriteLn ('Señales TGTK_OBJECT');
  signal_connect ('destroy', GTK_SIGNAL_FUNC (@_signal_destroy_));
END;

El método 'sDestroy' es un método virtual. El problema está en que, aunque sobrecargue (OVERRIDE) dicho método en una clase hija, el procedimiento '_signal_destroy_' sigue llamando al método de la clase base (en este caso TGTK_OBJECT). Creo que el problema está en utilizar 'SELF' al llamar a 'gtk_signal_connect' pero, ¿de qué otra forma podría hacerlo sino?

Ñuño Martínez 12-07-2006 10:52:00

Solucionado
 
Me merezco unos capones por lerdo y tormo. Resulta que el código funciona perfectamente tal como está. ¿Cuál era el problema, entonces? Pues que a la hora de crear el objeto lo creaba de la clase base en lugar de la derivada, y por supuesto que llamaba a los métodos de la clase base... :o

Espero que después de este error estúpido me permitan continuar participando en el foro.

jam 12-07-2006 15:37:35

Me imagino que conecerás el wrapper de GTK con freepascal. Está bastante avanzado. ¿No te sirven?

Ñuño Martínez 12-07-2006 17:23:55

Encontré este en sourceforge y no me gusta. El código fuente es muy raro y no me extraña que le salgan esos bugs que dice la documentación (utiliza un preprocesador para generar el código fuente :confused:). Jamás vi nada igual.

Con la versión que viene en el FreePascal que me bajé (creo que es la última estable) sólo viene una traducción directa de C a Pascal. También viene la FCL (Free Component Library) que, al parecer, se basa en GTK pero tampoco me gusta...

En fin, cuando termine el programa que estoy haciendo tal vez publique mi wrapper para ObjectPascal/Delphi a ver si a alguien más le gusta.


La franja horaria es GMT +2. Ahora son las 10:33:48.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi