Typ von typlosen Parameter bestimmen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Typ von typlosen Parameter bestimmen

Beitrag von Bergmann89 »

Hey Leute,

ich würde gern sowas machen, nur ohne den 2. Parameter:

Code: Alles auswählen

procedure DoSomethingWithMyObjectAndNil(var obj; const aTypeInfo: PTypeInfo); 
var
  myObj: TMyObject;
begin
  myObj := nil;
  if aTypeInfo^.Kind = tkInterface then begin
    if not Supports(IUnknown(obj), TMyObject, myObj) then
      raise Exception.Create('interface is not implemented by TMyObject');
    IUnknown(obj) := nil;
  end else if (aTypeInfo^.Kind = tkClass) then begin
    if (TObject(obj) is TMyObject) then
      myObj := (TObject(obj) as TMyObject);
    TObject(obj) := nil;
  end else
    raise Exception.Create('only objects or interfaces supported');
  myObj.DoSomething;
end;
Das sollte dann also ungefähr so aussehen:

Code: Alles auswählen

var 
  o1: TMyObject;
  o2: TMyInheritedObject;
  i: IMyObject;
DoSomethingWithMyObjectAndNil(o1);
DoSomethingWithMyObjectAndNil(o2);
DoSomethingWithMyObjectAndNil(i);
Ich würde mir halt gern sparen jedesmal das TypeInfo mit als 2. Parameter zu übergeben, ist weniger Schreibarbeit und sieht einfacher aus. Der Grund warum ich das so mache ist ein ralativ komplexer Klassenbaum bei dem über verschiedene Interfaces die Sichtbarkeiten bzw. Zugriffe auf bestimmte Funktionen gesteuert werden. Am Ende muss dann das zugrundeliegende Objekt sauber aus dem Konstrukt entfernt werden und die Variable soll genilt werden (deshalb der untyped var parameter). Das ist natürlich nur die Kurzversion, das ganze ist weitaus komplexer, deshalb auch kein RefCount für die Interfaces. Gibts ne Möglichkeit das so wie oben beschrieben umzusetzen?

MfG & Thx Bergmann.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6768
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Typ von typlosen Parameter bestimmen

Beitrag von af0815 »

Schau so aus, als würdest du 'variants' haben wollen.

Wenn es Objekte sind, so wäre es eventuell über das Auslesen der VMT (Siehe Beschreibung VMT) möglich den Klassennamen zu bekommen. Wenn es über die geht, so ist IMHO fraglich ob dieser 'Komfort' nicht zuviel unnötig an Leistung frisst.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: Typ von typlosen Parameter bestimmen

Beitrag von Bergmann89 »

Hey,

Variants gehen ja nur mit einfachen Typen. Ich hab aber nur Objekte und Interfaces.
Wie du das mit der VMT meinst versteh ich noch nicht ganz. Wenn ich die Infos auslesen will brauch ich doch auch erstma die Klasse des Objekts. Ich hab aber nur den Zeiger auf das Objekt oder auf das Interface.

MfG Bergmann.

Benutzeravatar
theo
Beiträge: 10867
Registriert: Mo 11. Sep 2006, 19:01

Re: Typ von typlosen Parameter bestimmen

Beitrag von theo »

@Bergmann: Sieht für mich nach Murks aus. Bist du sicher, dass es keinen besseren Weg gibt?

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: Typ von typlosen Parameter bestimmen

Beitrag von Bergmann89 »

Hey,

es gibt verschiedene Lösungsansätze, aber da hat mir bis jetzt noch keiner so richtig gefallen. Das ganze ist ein Resourcen Manager. Er läd und hält die Instancen von angeforderten Resourcen. Wenn man die Resource nicht mehr braucht gibt man die Resource über den Manager wieder frei. Der entscheidet dann ob die Instanz der Resource freigegeben werden kann, oder ob die noch wo anders benötigt wird. Eine Resource kann durch ein Interface oder direkt durch ein Objekt abgebildet werden. Wenn die Resource vom Nutzer freigegeben wird soll beim Aufruf der Funktion auch gleich die Variable in der die Resource gespeichert wurde genilt werden. Also praktisch wie bei FreeAndNil. Jetzt hab ich folgende alternativen:
- ReleaseResource 100 mal überladen, für jede Resource einmal
- den Typ der Resource mit an ReleaseResource übergeben (siehe oben)
- 2 überladene Methoden, die den Typ automatich bestimmen: procedure(var x; const o: TObject); und procedure(var x; const i: IUnknown);
- die Resource einfach by Value übergeben und jedesmal per Hand nilen
Bis jetzt war es immer so, das Resourcen immer Objekte waren. Das mit den Interfaces ist neu dazu gekommen. Deshalb ging das bis jetzt auch immer mit dem typlosen var Paramter. Den hab ich dann immer in ein TObject gecastet und dann entsprechend freigegeben und genilt, aber wenn ich jetzt ein Interface übergebe und das in ein Objekt caste knallt es. So sah das bis jetzt aus:

Code: Alles auswählen

procedure TengResourceManager.ReleaseResource(var aResource);
var
  obj: TObject;
  res: IengLoadableResource;
begin
  obj := TObject(aResource);
  TObject(aResource) := nil;
  if not Assigned(obj) then
    exit;
  if (obj is IengLoadableResource) then begin
    res := (obj as IengLoadableResource);
    ReleaseResourceIntern(res);
  end else
    obj.Free;
end;
Ich such jetzt eine Lösung um das Ganze typsicher zu bekommen. Der Nutzer fragt eine Resource an und gibts sie über den Resourcen Manager auch wieder frei. Er soll sich nicht darum kümmern müssen was für eine Resource das ist, den da entstehen wieder Fehler: War das jetzt ein Interface? Muss ich das nur nil setzen? Oder war das doch ein Objekt und ich muss vorher Free aufrufen? Das soll alles das ReleaseResource erledigen. Ist sogesehen eigentlich nur ne Schönheits-Sache. Lösungen die funktionieren würden hab ich ja. Ich präferiere zur Zeit Lösung 3, da würde der Aufruf wie folgt aussehen:

Code: Alles auswählen

ReleaseResource(fRes, fRes); //sieht lustig aus oder^^
MfG Bergmann.

Socke
Lazarusforum e. V.
Beiträge: 3177
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Typ von typlosen Parameter bestimmen

Beitrag von Socke »

Bergmann89 hat geschrieben:Ich hab aber nur Objekte und Interfaces.
Für Objekte kannst du ausnahmslos TObject verwenden.

Bei Interfaces wird das ganze etwas schwieriger. Hier wäre es denkbar, dass das Interface die Speicheradresse der freizugebenden Objektvariablen ermitteln kann. Diese kann man dann freigeben.

Code: Alles auswählen

type tobjref = ^TObject;
procedure DoSth(var i: IMyInterface);
var r: tobjref;
begin
  r := i.GetObjRef;
  FreeAndNil(r^);
  i := nil;
end;
Hier musst du natürlich sicherstellen, dass es keine weiteren Referenzen auf das Objekt gibt, die noch verwendet werden. Möglich wäre z.B. das Singleton-Pattern.

Code: Alles auswählen

type tmyobj = class(TObject, IMyinterface)
singleton: Tmyobj;
function GetObjRef: Tobjref;
end;
// ....
function tmyobj.GetObjRef: tobjref;
begin
  Result := @tymobj.singleton;
end;
 
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: Typ von typlosen Parameter bestimmen

Beitrag von Bergmann89 »

@Socke: ich glaub du hast das Problem nicht verstanden. An das Objekt komm ich ganz easy über den as-Operator, das ist aber nicht der Punkt. Ich suche nach einer Methode (bzw. mehreren überladenen Methoden) die das Interface bzw. das Objekt als Parameter erwarten (by reference) und dann das Interface bzw. das Objekt freigeben und die Variablen nilen. Und da var Parameter vom Typ immer übereinstimmen müssen (und ich keine abgeleiteten Typen übergeben kann) hatte ich das bis jetzt mit dem typlosen Parameter gelöst und dann einfach auf TObject gecastet. Das geht jetzt mit der Mischung aus Interfaces und Objects nicht mehr, weil ich nicht weiß ob ein TObject (oder Ableitung) oder ein IUnknown (oder Ableitung) übergeben wurde.
€: was hat Singelton damit zu tun?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Typ von typlosen Parameter bestimmen

Beitrag von mse »

Code: Alles auswählen

 
type
 TengResourceManager = class
  public
   procedure ReleaseResource(const aResource: pobject);
   procedure ReleaseResource(const aResource: pinterface);
 end;
 
{ TengResourceManager }
 
procedure TengResourceManager.ReleaseResource(const aResource: pobject);
begin
 freeandnil(aresource^);
end;
 
procedure TengResourceManager.ReleaseResource(const aResource: pinterface);
begin
 aresource^:= nil;
end;
 
{$typedaddress on}
 
procedure tmainfo.test(const sender: TObject);
var
 obj: tobject;
 intf: iinterface;
 manager: tengresourcemanager;
begin
 obj:= tobject.create;
 intf:= tinterfacedobject.create;
 manager:= tengresourcemanager.create;
 manager.releaseresource(@obj);
 manager.releaseresource(@intf);
 manager.free;
end;
 
Übrigens, das "as" beinhaltet ein zusätzliches "is" ("is" ist unter Umständen keine schnelle Operation) und ist überflüssig.

Code: Alles auswählen

 
  if (obj is IengLoadableResource) then begin
    res := (obj as IengLoadableResource);
->
  if obj is IengLoadableResource then begin
    res := IengLoadableResource(obj);
 

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: Typ von typlosen Parameter bestimmen

Beitrag von Bergmann89 »

Hey,

das mit den Pointer hatte ich auch schon probiert, allerdings ohne {$typedaddress on}, das kannte ich noch gar nicht. Jetzt gehts, Danke :)
Ich hab die gleich global aktiviert und noch 2 Codestellen gefunden wo die Zeiger falsch waren. Warum is so ne wichtige Option by default disabled?

MfG Bergmann.

martin_frb
Beiträge: 586
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: Typ von typlosen Parameter bestimmen

Beitrag von martin_frb »

Bergmann89 hat geschrieben:Hey,

das mit den Pointer hatte ich auch schon probiert, allerdings ohne {$typedaddress on}, das kannte ich noch gar nicht. Jetzt gehts, Danke :)
Ich hab die gleich global aktiviert und noch 2 Codestellen gefunden wo die Zeiger falsch waren. Warum is so ne wichtige Option by default disabled?

MfG Bergmann.
Weil Sie lange Zeit disabled war, und weil das aendern dazu fuehrt, das bestimmter Code andere Ergebnisse bringt (und damit nicht mehr funktioniert)

var
A: Array [1..99] of integer;

a[2] := 11;
writeln( PInteger( (@a[1] + 4) )^ );

funktioniert ohne {$typedaddress on}
Aber mit {$typedaddress on} schreibt es Die falsch Zahl.

ohne {$typedaddress on}
@a[1] ist untyped
@a[1] + 4 // 4 BYTES weiter = 1 integer weiter = @a[2]

mit {$typedaddress on}
@a[1] ist typed PInteger
@a[1] + 4 // 4 INTEGER weiter = @a[5]

Antworten