Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   tipo al que apunta un pointer (https://www.clubdelphi.com/foros/showthread.php?t=60928)

gushynet 19-10-2008 19:45:25

tipo al que apunta un pointer
 
Hola, a ver si podeis ayudarme con una duda relacionada con los pointers. Recientemente he empezado a mirarme el tema de la rtti y me he planteado la siguiente situación:

tengo un procedimiento o función con un parámetro de tipo pointer. La pregunta es si dentro de la rutina podría saber el tipo de dato al que apunta el pointer, ya que la unica informacion de la que dispongo es de la direccion de memoria donde se encuentra a lo que apunto(lo que quiera que sea). He mirado informacion sobre rtti pero no he visto ninguna documentacion relacionada por lo que me ha llevado a pensar que no se puede hacer y que en la situacion que expuse la rutina debe saber el tipo de dato que se le pasa a traves del pointer.


Gracias de antemano por la respuesta. Un saludo

roman 19-10-2008 20:38:35

No sé qué relación haya entre un apuntador y rtti, al menos no a primera vista. Un apuntador normalmente contiene la información del tipo de datos al que apunta en su propio tipo de datos, esto es, un PInteger, por ejemplo, está declarado como

Código Delphi [-]
type
  PInteger = ^TInteger;

esto es, como un apuntador a Integer, de manera que, en tiempo de compilación, se sabe a qué apunta.

No obstante, también hay apuntadores genéricos

Código Delphi [-]
var
  P: Pointer;

que pueden apuntar a cualquier cosa que tu les digas (mediante un moldeo de tipos), y el compilador, por lo general, te "creerá". Por ejemplo:

Código Delphi [-]
TForm(P).Show;

Aquí estás diciendo al compilador, que P apunta a un TForm, e invocas su método Show. Obviamente, en la ejecución saltará algún error en caso de que le hayas "mentido" al compilador.

Pero el punto aquí, es que ese tipo de apuntadores pueden apuntar a lo que sea, e incluso cambiar lo apuntado durante la ejecución, de manera que no creo que haya forma de saber el tipo de datos de lo que apuntan, aunque, desde luego, puedo estar equivocado.

// Saludos

coso 19-10-2008 21:21:11

Hola, tal como te dice roman, en principio no se puede saber a que dato apunta el puntero, pues este no es mas que una direccion de memoria. Hace poco estuve trabajando bastante con la RTTI sobre un componente: si explicas un poco mejor para que quieres la funcion o procedimiento, quiza te pueda echar una mano. Saludos.

seoane 19-10-2008 21:31:59

Hombre no me quiero meter con el rtti, porque no lo conozco. Pero si estamos hablando de objetos, podríamos intentar algo como esto:
Código Delphi [-]
var
  P: Pointer;
begin
  P:= Form1; // Un objeto cualquiera
  try
    if TObject(P) is TObject then
      ShowMessage(TObject(P).ClassName);
  except
    // Si llegamos aqui el puntero no era un objeto
  end;
end;

roman 19-10-2008 21:38:56

Ven. Yo dije que podía equivocarme :D

Eso está muy bien Domingo, al menos si nos restringimos al ámbito de clases, sí que se puede saber el tipo de datos, tal como atinadamente indicas.

// Saludos

gushynet 20-10-2008 00:11:39

Gracias por todas las explicaciones y sugerencias.

Estoy intentando hacer un procedimiento de asignacion para una clase. Idealmente este procedimiento debería tener dos parámetros: el nombre de la propiedad y el valor nuevo ya que estoy usando rtti.

De momento lo estoy intentando hacer para tipos simples, es decir, la clase solamente tendrá propiedades cuyos tipos son simples y no otros objetos.

El problema que veo es que el segunda parámetro puede ser de distinto tipo en funcion del tipo de propiedad a la cual queramos cambiar su valor.

No veo claro sobrecargar el procedimiento y hacer tantos procedimientos como tipos de datos simples haya por lo que pense en usar pointers: paso el dato a traves de un puntero genérico y me despreocupo del tipo de dato de la propiedad que quiero cambiar. Ya dentro del procedimiento, averiguo a que tipo de dato apunta el pointer y hago el casting adecuado para hacer la asignación.

Espero haberme explicado y gracias por la ayuda.

Un saludo

Salud y Delphi

seoane 20-10-2008 11:07:58

Si te entendi bien. El tipo lo puedes averiguar buscandolo en las propiedades del objeto de destino en vez de en el puntero que te pasan.

Por ejemplo, si te pasan el objeto A para fijar la propiedad B, solo tienes que saber de que tipo es B para suponer que el puntero que te pasan apunta a una variable del mismo tipo. Y en el peor de los casos, si te pasan un puntero a una variable de tipo incorrecto, saltara una excepcion al asignarlo a la propiedad lo que me parece un comportamiento muy correcto ya que nos esta señalizando que ha ocurrido un error.

coso 20-10-2008 11:36:41

Seria algo como esto

Código Delphi [-]
        try
        case typinfo.PropType(c,pn) of
        tkInteger     : SetOrdProp(c,pn,strtoint(pv));
        tkInt64       : SetInt64Prop(c,pn,strtoint64(pv));
        tkFloat       : SetFloatProp(c,pn,strtofloat(pv));
        tkChar,
        tkWChar,
        tkString,
        tkLString,
        tkWString     : SetStrProp(c,pn,GetString(pv));
        tkVariant     : SetVariantProp(c,pn,pv);
        tkMethod      : SetMethodProp(c,pn,m);
        tkEnumeration : SetEnumProp(c,pn,Trim(pv));
        tkSet         : SetSetProp(c,pn,Trim(pv));
        tkClass       : if (GetObjectPropClass(c,pn).ClassName = 'TStrings') then
                                (GetObjectProp(c,pn) as TStrings).CommaText := pv
                        else
                        if (GetObjectPropClass(c,pn).ClassName = 'TParams') then
                        begin
                                params := StringToParams(pv);
                                (GetObjectProp(c,pn) as TParams).Assign(params);
                                params.Free;
                        end
                        else SetObjectProp(c,pn,GetObject(nil,tpv));
        else
                        SetPropValue(c,pn,pv);
        end;
        except
        // error asignando propiedad.
        end;

tambien puedes mirar antes si la propiedad existe en el objeto destino. Te recomiendo que mires los tips de la pagina de neftali, donde hay otra variante de esta funcion y tambien una explicacion de PPropInfo. Tambien que mires sobre la funcion GetPropList y la unidad Typinfo.

PD: en el codigo, getobject es:

Código Delphi [-]
function GetObject(c : TObject; s : string) : TObject;
begin
          s := trim(s);
          if s = '' then result := c;
          if c = nil then c := Application;
          result := GetObject((c as TComponent).FindComponent(Copy(s,0,Pos('.',s)-1),Copy(s,Pos('.',s)+1));
end;

stringtoparams transforma una string a tipo TParams, GetStrings devuelve una string entre comillas y m es un TMethod. Saludos y a ver si te sirve.

gushynet 20-10-2008 17:23:33

Código Delphi [-]
type TObjeto = class(TObject)       
    .......   
 end;  
  ...........    
procedures TObjeto.Asignar(nombrePropiedad:AnsiString;datoNuevo:?);    begin       
.....  
 end


La funcion Asignar es un método del objeto por lo que no intervienen mas objetos que el que llama al método.

Esta funcion en principio deberia ser capaz de cambiar el valor de una de sus propiedades simples, es decir, propiedades como el caption de un TButton por ejemplo que es de tipo AnsiString.

Como dije en el correo anterior, el problema que se me plantea y por el que no puedo usar la informacion proporcionada por rtti es en el segundo parámetro del método asignar. De que tipo de dato es?, depende de la propiedad que indique en el primer parámetro. Lo que esta claro es que no existen funciones condicionales en la que el tipo de uno de sus parámetros venga en función de otro parámetro de la misma rutina.

El primer parámetro no tiene problema, es el nombre de la propiedad a la que quiero cambiar su valor.

Por tanto, las posibles soluciones que veo son:

* sobrecargar el procedimiento asignar:

- procedure TObjeto.asignar(NombrePropiedad:AnsiString;valor:integer)overload;
- procedure TObjeto.asignar(NombrePropiedad:AnsiString;valor:double)overload;
-.... y asi con todos los tipo simples.


* pasar el segundo parámetro como pointer y dentro de la rutina averiguo el tipo al que apunta el pointer (a un entero,a un real, booleano, string,....), hago el casting y realizo la asignacion.

Pero claro, ¿como me entero de a lo que apunta un pointer, si lo unico que puedo averiguar a través del pointer es la direccion que tiene almacenada pero no que es lo que representa?. Por esta razón creo que voy a optar por sobracargar la rutina Asignar, una por cada tipo de dato simple. Y mira que lo del pointer me parecia una solucion mas elegante, pero no se puede tener todo en la vida :), se puede tener sexo, alcohol pero no un pointer con mas conversacion.


Gracias por la ayuda. Un saludo.

Salud y delphi

coso 20-10-2008 17:30:46

pero...si buscas informacion sobre GetPropInfo encontraras que dando el nombre de la propiedad (tal cual, como una string) te devuelve que tipo de dato es aquella propiedad, con lo que ya tendrias resuelto que cast debes hacerle al puntero que pasas como segundo parametro. De todas maneras, puedes hacer que el segundo parametro sea string, y segun el tipo de propiedad, hacer strtofloat, strtoint o bien encontrar el objeto pertinente segun su nombre. Otra manera seria haciendo una combinacion de las dos:

- procedure TObjeto.asignar(NombrePropiedad:AnsiString;valor:string)
- procedure TObjeto.asignar(NombrePropiedad:Ansistring;valor : TObject);

el que tiene valor como string luego podrias transformarlo segun te convenga a strtofloat,etc...y el que tenga valor : TObject hacer un cast segun el tipo de clase que sea la propiedad. Incluso, en vez de string, pasar valor : variant, con lo que lo simplificarias bastante.

coso 20-10-2008 17:32:51

de la misma manera con typinfo.PropType(c,pn), donde c seria el objeto y pn el nombre de la propiedad...te devuelve el tipo de dato que es aquella propiedad.

coso 20-10-2008 18:28:25

es mas...creo que lo que tu querias hacer, es lo que hace exactamente el tipo 'variant'. Saludos

gushynet 20-10-2008 20:08:41

gracias por la ayuda, tenias razón coso.

Un saludo.

Salud y Delphi


La franja horaria es GMT +2. Ahora son las 16:29:39.

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