FreePascal && Interfaces

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
cybersnacker
Beiträge: 8
Registriert: Do 26. Apr 2007, 02:04
OS, Lazarus, FPC: Debian
CPU-Target: 32/64
Wohnort: 23566 Lübeck

FreePascal && Interfaces

Beitrag von cybersnacker »

Hallo @ liebes Forum...

Ich hoffe bei folgender Problemstellung kann mir jemand etwas Licht in's Dunkle bringen... Bei einem groesseren Projekt verfolge ich die hier in etwa zusammengefasste Struktur und treffe immer auf denselben Fehler:

Code: Alles auswählen

{
 
fpc -i
 
Free Pascal Compiler version 2.2.4
 
Compiler Date: 2009/10/18
Compiler CPU Target: i386
 
}
 
program Project1;
 
{$mode objfpc}
 
uses
  Classes, SysUtils;
 
type
 
ITest1 = interface
['{5BC06B9E-A582-4AFD-BC5D-2C78DFBC13FE}']
  procedure Test;
end;
 
ITest2 = interface
['{AB6094A7-F345-4E7F-B368-FDF7574B4115}']
  procedure Test;
end;
 
TTest1 = class(TInterfacedObject, ITest1)
private
  fCount1 : integer;
public
  procedure Test;
  property  Count: integer read fCount1 default 0;
end;
 
TTest2 = class(TTest1, ITest2)
private
  fCount2 : integer;
 
public
  procedure Test;
  property  Count: integer read fCount2 default 0;
end;
 
procedure TTest1.Test;
begin
  inc(fCount1);
end;
 
procedure TTest2.Test;
begin
  inc(fCount2);
end;
 
procedure Convert(aClass: TObject; out aOut: TObject);
begin
  aOut := nil;
  // folgender IF Bedinung Kommentierung entfernen und es haengt
  // if Supports(aClass, ITest2) then
    aOut := aClass;
end;
 
var count:       integer;
    fTestClass1: TTest1;
    fTestClass2: TTest2;
    fTestObject: TObject;
    fTestIntf:   ITest2;
 
begin
  fTestClass2 := TTest2.Create;
  fTestClass1 := fTestClass2;
 
  Convert(fTestClass1, fTestObject);
 
  {$ASSERTIONS ON}
  Assert(assigned(fTestObject), 'not assigned');
  {$ASSERTIONS OFF}
 
  fTestIntf := fTestObject as ITest2;
  fTestIntf.Test;
 
  if Supports(fTestObject, ITest1) then begin
    (fTestObject as ITest1).Test;
    writeln(fTestClass2.Count);
  end;
 
  if Supports(fTestObject, ITest2) then begin
    (fTestObject as ITest2).Test;
    writeln(fTestClass2.Count);
  end;
end.
Mein Problem beschreibt sich darin, dass ich viele verschiedene Objekte verschiedener Herkunft (Definition/Basisklasse) habe, die jedoch alle einer hierarchischer Struktur klassifizierbar sein sollen. Deswegen habe ich mich fuer Interfaces entschieden, um so den einzelnen Klassen eine gemeinsame Schnittstelle zu verpassen, auf dessen Basis ich in bestimmten Methoden, ohne die Objekte genau typisieren zu wollen, auf gemeinsame Merkmale bzw. Methoden zugreifen kann.

So das ganze kann dann im Haertefall obiger Zusammenfassung aussehen, gehe ich jedoch mit der Supports Methode ran, dann knallt es. Versuche ueber den IS Operator sind ebenfalls gescheiter. Nun halt die beliebte Frage an Euch, wo ist der Fehler?!?

Gruss
// Benno
Zuletzt geändert von monta am Mo 9. Nov 2009, 13:10, insgesamt 1-mal geändert.
Grund: Richtige Syntaxhervorhebung

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

Re: FreePascal && Interfaces

Beitrag von theo »

Ich weiss nicht genau worauf du hinauswillst, aber wenn sonst keiner was sagt....

Probier doch mal, eine solche Dekl.

statt
fTestClass2: TTest2;
nimm mal
fTestInt2:ITest2;

und dann
fTestInt2 := TTest2.Create;

etc.. z.B.

Code: Alles auswählen

procedure Convert(aClass: IInterface; out aOut: IInterface);
begin
  aOut := nil;
   if Supports(aClass, ITest2) then
    aOut := aClass;
end;

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: FreePascal && Interfaces

Beitrag von Patito »

Also zunächst einmal würde ich Dir raten die Objekte nicht von TInterfacedObject abzuleiten.
Leider haben die Entwickler von Delphi wohl Interfaces an sich nicht wirklich verstanden und haben da beim Design ziemlich Mist gebaut.

Wenn man ernsthaft Interfaces benutzen will denke ich ist der einzig vernünftige Weg dazu erst mal das eingebaute Ref-Counting komplett auszuhebeln. Leider gibt es dabei einige Sachen zu beachten, aber alles andere ist vom Prinzip her einfach nur grober Unsinn.

Also:
Von TObject ableiten und folgende Funktionen selbst implementieren:

Code: Alles auswählen

function QueryInterface(const iid : tguid;out obj) : longint; stdcall;
  function _AddRef : longint;stdcall;
  function _Release : longint;stdcall;

Code: Alles auswählen

TTest1 = class(TObject, ITest1)
private
  fCount1 : integer;
protected
  function QueryInterface(const iid : tguid;out obj) : longint; stdcall;
  function _AddRef : longint;stdcall;
  function _Release : longint;stdcall;
public
  procedure Test;
  property  Count: integer read fCount1 default 0;
end;
 
function TTest1.QueryInterface(const iid: tguid; out obj): longint; stdcall;
begin
  if getinterface(iid,obj) then
    result:=0
  else
    result:=longint(E_NOINTERFACE);
end;
 
function TTest1._AddRef: longint; stdcall;
begin
 Result := -1;
end;
 
function TTest1._Release: longint; stdcall;
begin
  Result := -1;
end;
Damit sollte es schon deutlich besser klappen...

Vielleicht später mehr zu den Sachen, die man sonst noch beachten muss...

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: FreePascal && Interfaces

Beitrag von mse »

Patito hat geschrieben: Also:
Von TObject ableiten und folgende Funktionen selbst implementieren:
Alternative: Corba style interface ( {$interfaces corba} ) die sind nicht Referenz-gezählt, gibt es nur in FPC.

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: FreePascal && Interfaces

Beitrag von Patito »

mse hat geschrieben:
Patito hat geschrieben: Also:
Von TObject ableiten und folgende Funktionen selbst implementieren:
Alternative: Corba style interface ( {$interfaces corba} ) die sind nicht Referenz-gezählt, gibt es nur in FPC.
Perfekt, sowas ist natürlich das richtigere.

Nachtrag:
getinterface() funktioniert mit Corba-Interfaces anscheinend erst seit FPC 2.2.4. (Mal sehen, ob es auch richtig funktioniert...)

Habe gerade in Lazarus einige Sachen mit den COM-Interfaces getestet, die eigentlich AVs produzieren müssen. (_Release von freigegebenen Objekten etc...). Der Debugger hat mir aber keine Fehlermeldungen ausgespuckt.
Gibt es eine Compiler-Option mit der man schnell Memory-Checks ala FullDebugMode von FastMM einschalten kann?

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: FreePascal && Interfaces

Beitrag von mse »

Patito hat geschrieben: getinterface() funktioniert mit Corba-Interfaces anscheinend erst seit FPC 2.2.4.
MSEgui hat die Funktion getcorbainterface() in lib/common/mseclasses.pas welche auch mit älteren FPC Versionen funktioniert.
Gibt es eine Compiler-Option mit der man schnell Memory-Checks ala FullDebugMode von FastMM einschalten kann?
Kompiliere mit -gh, dies linkt den heaptrace memory-manager ein. Den gibt es in FPC übrigens schon seit Jahren, lange bevor Delphi etwas ebenbürtiges aufzuweisen hatte.

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

Re: FreePascal && Interfaces

Beitrag von theo »

Patito hat geschrieben:Also zunächst einmal würde ich Dir raten die Objekte nicht von TInterfacedObject abzuleiten.
Leider haben die Entwickler von Delphi wohl Interfaces an sich nicht wirklich verstanden und haben da beim Design ziemlich Mist gebaut.

Wenn man ernsthaft Interfaces benutzen will denke ich ist der einzig vernünftige Weg dazu erst mal das eingebaute Ref-Counting komplett auszuhebeln. Leider gibt es dabei einige Sachen zu beachten, aber alles andere ist vom Prinzip her einfach nur grober Unsinn.
Kannst du das etwas ausführen? Bzw. Info-Quellen angeben? Klingt ja schon ziemlich heftig... :wink:

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: FreePascal && Interfaces

Beitrag von mse »

theo hat geschrieben:
Patito hat geschrieben:aber alles andere ist vom Prinzip her einfach nur grober Unsinn.
Kannst du das etwas ausführen? Bzw. Info-Quellen angeben? Klingt ja schon ziemlich heftig... :wink:
Recht hat er. :-)
Referenz gezählte Interface sind ein Horror. Nie weiss man wo, wann, ob überhaupt, oder warum nicht, oder eben doch, die Instanzen abgeräumt werden.

Martin

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

Re: FreePascal && Interfaces

Beitrag von theo »

mse hat geschrieben: Recht hat er. :-)
Referenz gezählte Interface sind ein Horror. Nie weiss man wo, wann, ob überhaupt, oder warum nicht, oder eben doch, die Instanzen abgeräumt werden.
Ich sag ja nichts dagegen, ich wollte es nur wissen.
Ist das was anderes als referenzgezählte Strings? Die bemängelt ja keiner afaik.

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: FreePascal && Interfaces

Beitrag von mse »

theo hat geschrieben: Ist das was anderes als referenzgezählte Strings? Die bemängelt ja keiner afaik.
Ja, das ist etwas anderes. In tanyclass.destroy() passiert meist wesentlich mehr als das simple Freigeben des Speichers. Und häufig implementiert eine Klasse mehrere Interface, kannst du dir das Durcheinander vorstellen? Und dann verwendet FPC manchmal Zwischenvariablen, wo es Delphi nicht tut...

cybersnacker
Beiträge: 8
Registriert: Do 26. Apr 2007, 02:04
OS, Lazarus, FPC: Debian
CPU-Target: 32/64
Wohnort: 23566 Lübeck

Re: FreePascal && Interfaces

Beitrag von cybersnacker »

danke fuer den Aufwind in diesem Thread:)

Nun meine Gedanken gingen auch in Richtung reference counting. Mir die bis dato noch unbekannt, vom fpc ebenfalls unterstuetzte Struktur CORBA scheint auf den ersten Blick eine Alternative zu sein, leider sind wohl der as Operator sowie die Supports Methode nicht fuer diese Art implementiert, fehlende Unterstuetzung der GUID Struktur?!?

Naja, der erste Beitrag von Patito wird als sehr hilfreich notiert und loest zumindest mein Problem soweit:)

// Benno

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: FreePascal && Interfaces

Beitrag von mse »

cybersnacker hat geschrieben:leider sind wohl der as Operator sowie die Supports Methode nicht fuer diese Art implementiert, fehlende Unterstuetzung der GUID Struktur?!?
Neuere FPC Versionen sollten dies können AFAIK, oder verwende die MSEgui Funktion getcorbainterface().
http://mseide-msegui.svn.sourceforge.ne ... s?view=log

Dort gibt es auch tnullinterfacedobject und tnullinterfacedpersistent. Für das Erhalten eines Interace-Pointers einer bekannten Klasse reicht

Code: Alles auswählen

meineinterfacevariable:= Imeininterface(meineklasseninstanz);
Naja, der erste Beitrag von Patito wird als sehr hilfreich notiert und loest zumindest mein Problem soweit:)
Eine Warnung noch, bei _release() ist die Instanz möglicherweise schon freigegeben, Valgrind wird da vermutlich reklamieren. Und im Hinterkopf habe ich, dass dadurch sogar ernsthafte Probleme entstehen könnten, die Zusammenhänge sind mir aber nicht mehr präsent. Vermutlich ist die Verwendung von Corba Interface sicherer.

Martin

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: FreePascal && Interfaces

Beitrag von Patito »

mse hat geschrieben:
cybersnacker hat geschrieben:leider sind wohl der as Operator sowie die Supports Methode nicht fuer diese Art implementiert, fehlende Unterstuetzung der GUID Struktur?!?
Neuere FPC Versionen sollten dies können AFAIK, oder verwende die MSEgui Funktion getcorbainterface().

Eine Warnung noch, bei _release() ist die Instanz möglicherweise schon freigegeben, Valgrind wird da vermutlich reklamieren. Und im Hinterkopf habe ich, dass dadurch sogar ernsthafte Probleme entstehen könnten, die Zusammenhänge sind mir aber nicht mehr präsent. Vermutlich ist die Verwendung von Corba Interface sicherer.
Statt Supports() und "as" kann man jetzt bei Corba-Interfaces vermutlich einfach nur getinterface() verwenden. Damit hat man dann die Probleme der COM-Interfaces vom Hals...

Die Warnung bezüglich _release() kann ich nur bestätigen. Ohne Debugging mit einem auf Paranoia eingestellten Speicher-Manager sind COM-Interfaces viel zu gefährlich. Bevor man ein Objekt freigibt muß man ALLE Interface-Referenzen auf das Objekt aus dem Speicher entfernen, sonst knallts irgendwann. Und ohne geübten Blick übersieht man gerne ein paar implizite Referenzen, die der Compiler irgendwann mal auf den Stack gelegt hat...
Ist aber immer noch deutlich einfacher, als mit Ref-Counting alle Interface-Referenzen in der gesamten Anwendung immer in exakt der richtigen Reihenfolge entsorgen zu müssen..

// Regards Andreas D.

Patito
Beiträge: 203
Registriert: Di 22. Sep 2009, 13:08
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: FreePascal && Interfaces

Beitrag von Patito »

theo hat geschrieben:
mse hat geschrieben: Recht hat er. :-)
Referenz gezählte Interface sind ein Horror. Nie weiss man wo, wann, ob überhaupt, oder warum nicht, oder eben doch, die Instanzen abgeräumt werden.
Ich sag ja nichts dagegen, ich wollte es nur wissen.
Ist das was anderes als referenzgezählte Strings? Die bemängelt ja keiner afaik.
Der Unterschied ist, dass Strings untereinander keine Referenzen aufeinander halten. Für Datentypen, die selbst keine Referenzen auf irgendetwas halten ist Ref-Counting ganz gut. Für Objekte im allgemeinen ist das aber eher unfug.
Für COM waren die Interfaces wohl dafür gedacht immer genau eine API-zu kapseln (z.B. das ganze MS-Word), das geht dann mit Ref-Counting eine Weile lang gut. Aber wenn man heutzutage nur ein paar ganz banale Design-Patterns mit ein paar Interfaces hat, funktionert sowas einfach nicht mehr...

Wenn man z.B. einen Baum hat und die Knoten untereinander noch ein paar Referenzen aufeinander haben. Da muß man beim Entsorgen der Struktur folgendes machen:

a) ohne Ref-Counting:
Einfach alle Knoten Löschen

b) mit Ref-Counting:
Alle Referenzen analysieren und dann in exakt der richtigen Reihenfolge entfernen.

Hm. Info-Quellen sind schwierig. Wenn man die Postings der B-Trolle in den Delphi-Foren zum Thema nimmt und in jeden Satz ein großes NICHT einbaut liegt man nicht sonderlich falsch... :)

lrlr
Beiträge: 127
Registriert: Di 3. Nov 2009, 09:48

Re: FreePascal && Interfaces

Beitrag von lrlr »

also ganz so stimmt das aber nicht..

ref-counting bei interfaces macht schon sinn (halt nicht immer)

>Bevor man ein Objekt freigibt muß man ALLE Interface-Referenzen auf das Objekt aus dem Speicher entfernen, sonst knallts irgendwann.

genau umgekehrt, wenn man ein objekt freigibt, und es gibt noch interface referenzen darauf, DANN knallts aufjedenfall ...

meine meinung:

wenn man refcounted interfeces hat, darf man auch KEINE referenz auf das object selber haben..

freigeben darf man die objects selber ja sowieso nicht, die werden ja automatisch freigegeben..

für bäume würd es noch "week references" geben, da muss man dann natürlich wieder aufpassen..

Antworten