Implementiert eine Komponente ein Interface?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut

Implementiert eine Komponente ein Interface?

Beitragvon braunbär » 2. Okt 2018, 15:02 Implementiert eine Komponente ein Interface?

Ich habe einige eigene Komponenten entwickelt (von Standard LCL Komponenten abgeleitet), die alle das Interface IfdData implementieren sollen. Eine der Interface Methoden heisst z.B. Clear.

Jetzt würde ich gerne zur Laufzeit alle Komponenten einer Form durchgehen und bei den Komponenten, die mein Interface unterstützen, die Methode Clear aufrufen. Komponenten, die das Interface nicht unterstützen, sollen dabei einfach übergangen werden (die haben ja keine Clear Methode). Ich habe jetzt eine Weile herumprobiert, aber ich komme nicht drauf, wie man das syntaktisch richtig umsetzt.
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon mse » 2. Okt 2018, 15:23 Re: Implementiert eine Komponente ein Interface?

TObject.GetInterface() dient zu diesem Zweck:
https://www.freepascal.org/docs-html/cu ... rface.html
Das Interface muss eine GUID (bei COM-Interface) oder einen ID-string (bei Corba-Interface) haben.
mse
 
Beiträge: 2013
Registriert: 16. Okt 2008, 09: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
Nach oben

Beitragvon braunbär » 2. Okt 2018, 15:49 Re: Implementiert eine Komponente ein Interface?

Das schaut so aus, als wäre es, was ich brauche - aber wie ich es verwenden muss, ist mir auch nach dem Lesen der Dokumentation noch immer nicht klar.

1. Wie komme ich zum Parameter GUID?
Ich deklariere das Interface beispielsweise als
IfdData = Interface ['{0AB00115-0734-0184-C000-0C5600052148}'']
(Nebenbei: Gibt es eigentlich irgend welche Richtlinien, wie man den GUID String aufbauen soll, oder macht man das völlig willkürlich? In der Delphi-IDE gibt es glaube ich sogar ein Tastaturkürzel, das einen GUID String generiert)
Woher bekomme ich die GUID Variable - oder muss ich den GUID-String zu Fuß in den GUID Typ konvertieren?

Und was übergebe ich im Parameter obj? Die Komponente selbst steht doch schon vor dem Punkt: abc.GetInterface( guid, ????); Was muss denn da hin? obj ist ein OUT Parameter.
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon mse » 2. Okt 2018, 16:23 Re: Implementiert eine Komponente ein Interface?

braunbär hat geschrieben:Das schaut so aus, als wäre es, was ich brauche - aber wie ich es verwenden muss, ist mir auch nach dem Lesen der Dokumentation noch immer nicht klar.

1. Wie komme ich zum Parameter GUID?
Ich deklariere das Interface beispielsweise als
IfdData = Interface ['{0AB00115-0734-0184-C000-0C5600052148}'']

Dann kannst du
Code: Alles auswählen
 
 if <objektinstanz>.getinterface('{0AB00115-0734-0184-C000-0C5600052148}',<interfacevariable>) then begin
  tue etwas mit dem interface
 end;
 

verwenden.
(Nebenbei: Gibt es eigentlich irgend welche Richtlinien, wie man den GUID String aufbauen soll, oder macht man das völlig willkürlich?

Diese ID soll "Globally Unique" also welt- oder universumweit einmalig sein.
In der Delphi-IDE gibt es glaube ich sogar ein Tastaturkürzel, das einen GUID String generiert)

Das gibt es in Lazarus sicher auch. In MSEide wäre es RightClick-'Insert GUID'.
Woher bekomme ich die GUID Variable - oder muss ich den GUID-String zu Fuß in den GUID Typ konvertieren?

Siehe oben. Eine andere Option ist
Code: Alles auswählen
 
 if <objektinstanz>.getinterface(stringtoguid('{0AB00115-0734-0184-C000-0C5600052148}'),<interfacevariable>) then begin
  tue etwas mit dem interface
 end;
 

EDIT: oder besser
Code: Alles auswählen
 
 if <objektinstanz>.getinterface(<interfacetyp>,<interfacevariable>) then begin
  tue etwas mit dem interface
 end;
 

welches direkt mit TGUID arbeitet.
Und was übergebe ich im Parameter obj? Die Komponente selbst steht doch schon vor dem Punkt: abc.GetInterface( guid, ????); Was muss denn da hin? obj ist ein OUT Parameter.

"obj" ist die Interface Variable.
PS: Falls du COM-Interface verwendest (die Standardeinstellung), solltest du dir überlegen, ob nicht CORBA-Interface besser geeignet wären.
Zuletzt geändert von mse am 3. Okt 2018, 07:35, insgesamt 2-mal geändert.
mse
 
Beiträge: 2013
Registriert: 16. Okt 2008, 09: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
Nach oben

Beitragvon braunbär » 2. Okt 2018, 19:41 Re: Implementiert eine Komponente ein Interface?

Danke für die Antwort, mit den Infos sollte ich weiterkommen.

Ich benötige die Interfaces in dem Framework hier eigentlich nur, um auf einer abstrakten Ebene manche Aufgaben für die Komponenten einer Form automatisch erledigen zu können, die die entsprechende Funkionalität bereitstellen. Wenn es auf der Form noch andere Komponenten gibt, dann muss sich der Programmierer der Form (zur Zeit meistens auch ich selbst) in einer Callback Routine um diese Komponenten individuell kümmern.

Keine Ahnung, ob Corba da irgend einen Vorteil bieten würde. Es geht hier explizit nicht um Schnittstellen nach außen zu anderen Programmen.

Was ich gerne wissen würde, ist noch, ob die Prüfung auf Vorhandensein des Interfaces zeitaufwändig ist - ich vermute aber eher (und hoffe), dass das nicht der Fall ist.


Edit: Nochmals danke, das hat jetzt auf Anhieb funktioniert.
Zuletzt geändert von braunbär am 2. Okt 2018, 20:29, insgesamt 1-mal geändert.
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon sstvmaster » 2. Okt 2018, 19:58 Re: Implementiert eine Komponente ein Interface?

GUID Tastenkürzel: Shift+Strg+G siehe Menü -> Quelltext -> Einfügen allgemein -> GUID einfügen

selbst eine erzeugen:
Code: Alles auswählen
var
  T : TGUID;
 
begin
  CreateGUID(T);
  ShowMessage(GUIDToString(T));
end;
OS: Windows 7 32/64bit
Lazarus 1.8.4, 32bit
Lazarus 2.1.0 Trunk 3.3.1, 32bit
sstvmaster
 
Beiträge: 93
Registriert: 22. Okt 2016, 22:12
OS, Lazarus, FPC: Lazarus 1.8.4 + 2.1.0 Trunk 3.3.1 / Win32, Windows 7 32+64bit | 
CPU-Target: 32Bit
Nach oben

Beitragvon braunbär » 2. Okt 2018, 22:06 Re: Implementiert eine Komponente ein Interface?

Gut zu wissen.
Habs gerade ausprobiert. So schön wie meine GUID sind die aber nicht :mrgreen:
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon mse » 3. Okt 2018, 07:25 Re: Implementiert eine Komponente ein Interface?

braunbär hat geschrieben:Danke für die Antwort, mit den Infos sollte ich weiterkommen.

Ich benötige die Interfaces in dem Framework hier eigentlich nur, um auf einer abstrakten Ebene manche Aufgaben für die Komponenten einer Form automatisch erledigen zu können, die die entsprechende Funkionalität bereitstellen. Wenn es auf der Form noch andere Komponenten gibt, dann muss sich der Programmierer der Form (zur Zeit meistens auch ich selbst) in einer Callback Routine um diese Komponenten individuell kümmern.

Dann würde ich auf jeden Fall CORBA einsetzen.
Keine Ahnung, ob Corba da irgend einen Vorteil bieten würde. Es geht hier explizit nicht um Schnittstellen nach außen zu anderen Programmen.

Die üblichen COM -Interface sind referenzgezählt und vertragen sich schlecht mit aus dem Programm aufgerufenem destroy(), speziell die Kombination TComponent/COM-Interface ist ein Albtraum. CORBA-Interface haben diesen Nachteil nicht, da hat man alles unter Kontrolle; durch die fehlende Referenzzählung sind sie erst noch schneller.
Was ich gerne wissen würde, ist noch, ob die Prüfung auf Vorhandensein des Interfaces zeitaufwändig ist - ich vermute aber eher (und hoffe), dass das nicht der Fall ist.

Die Suche in den Tabellen ist aufwendig. CORBA hat den Vorteil, dass als ID ein beliebiger auch kurzer String verwendet werden kann, der lediglich in der Applikation einmalig sein muss. MSEide erzeugt solche ID's mit RightClick-'Insert UID'.
Schneller als die Tabellensuche zur Laufzeit ist Typenkonvertierung zur Kompilierzeit wenn der Klassentyp bekannt ist.
Code: Alles auswählen
 
type
{$interfaces corba}
 itest = interface ['AA']{0}  //ID not used in example.
  procedure test();
 end;
 
 ttest = class(itest)
  protected
   procedure test();
 end;
[...]
var
 t1: ttest;
[...]
 itest(t1).test();
 

Dabei wird die ID nicht benötigt. Ist die Tabellensuche unumgänglich, empfehlen sich ID-Konstanten in der Form nummer.framework.
Code: Alles auswählen
 
const
 id_test = 'AA.braunbaer';{0}
type
{$interfaces corba}
 itest = interface [id_test]
  procedure test();
 end;
 

In MSEgui liegen alle ID's zentral in der Datei lib/common/kernel/mseinterfaces.pas.
Die Tabellenabfrage ist auch in der Form
Code: Alles auswählen
 
var
 t1: ttest;
 intf1: itest;
[...]
 if t1.getinterface(itest,intf1) then begin
  intf1.test();
 end;
 

möglich.
mse
 
Beiträge: 2013
Registriert: 16. Okt 2008, 09: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
Nach oben

Beitragvon braunbär » 4. Okt 2018, 13:17 Re: Implementiert eine Komponente ein Interface?

Momentan funktioniert es mit den standard COM Interfaces. Ich werde aber Corba auch noch ausprobieren. Wenn ich es richtig verstehe, muss ich nur in allen Units, die so ein Interface verwenden, {$interfaces corba} vor die erste Verwendung/Deklaration einfügen, und dann kann ich, wenn ich will, statt der GUID auch einen kürzeren Namen verwenden.
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon mse » 4. Okt 2018, 13:27 Re: Implementiert eine Komponente ein Interface?

Ja, und es gibt keine Probleme mit Referenzzählung.
mse
 
Beiträge: 2013
Registriert: 16. Okt 2008, 09: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
Nach oben

Beitragvon Warf » 4. Okt 2018, 17:40 Re: Implementiert eine Komponente ein Interface?

So ne ganz dumme frage, warum verwendet man nicht einfach den is operator?
Code: Alles auswählen
if MyObj is ISomeInterface then
  (MyObj as ISomeInterface).SomeInterfaceMethod;


Das macht intern zwar auch den kram mit den GUIDs aber als entwickler muss man sich nicht um die guids kümmern. Ledeglich wenn man ein Interface schreibt einmal in Lazarus STRG+SHIFT+G drücken um die GUID zu generieren, danach kann man die GUID direkt wieder vergessen.

Der as Operator würde im notfall auch eine Exception werfen wenn die Klasse das Interface nicht implementiert. Daher kann man auch einfach mit einem try except arbeiten

Code: Alles auswählen
try
  ifVar := SomeObj as ISomeInterface
except
  on E: Exception do
    // kein ISomeInterface typ
end;
Warf
 
Beiträge: 985
Registriert: 23. Sep 2014, 16:46
Wohnort: Aachen
OS, Lazarus, FPC: Mac OSX 10.11 | Win 10 | FPC 3.0.0 | L trunk | 
CPU-Target: x86_64, i368, ARM
Nach oben

Beitragvon braunbär » 4. Okt 2018, 23:24 Re: Implementiert eine Komponente ein Interface?

Versteh ich auch nicht. Es wäre die intuitiv naheliegendste Variante, und es war eigentlich das erste Konstrukt, das ich versucht habe - erst nachdem der Compiler das nicht wollte, habe ich angefangen, die Doku durchzustöbern, und dann den Thread hier eröffnet.
braunbär
 
Beiträge: 252
Registriert: 8. Jun 2017, 17:21

Beitragvon Warf » 5. Okt 2018, 01:47 Re: Implementiert eine Komponente ein Interface?

braunbär hat geschrieben:Versteh ich auch nicht. Es wäre die intuitiv naheliegendste Variante, und es war eigentlich das erste Konstrukt, das ich versucht habe - erst nachdem der Compiler das nicht wollte, habe ich angefangen, die Doku durchzustöbern, und dann den Thread hier eröffnet.


Du musst eine guid erstellen (Strg+Shift+g in laz) damit müsste es funktionieren
Warf
 
Beiträge: 985
Registriert: 23. Sep 2014, 16:46
Wohnort: Aachen
OS, Lazarus, FPC: Mac OSX 10.11 | Win 10 | FPC 3.0.0 | L trunk | 
CPU-Target: x86_64, i368, ARM
Nach oben

Beitragvon mse » 5. Okt 2018, 06:17 Re: Implementiert eine Komponente ein Interface?

Warf hat geschrieben:So ne ganz dumme frage, warum verwendet man nicht einfach den is operator?

Performance?
Code: Alles auswählen
if MyObj is ISomeInterface then
  (MyObj as ISomeInterface).SomeInterfaceMethod;


Das macht intern zwar auch den kram mit den GUIDs aber als entwickler muss man sich nicht um die guids kümmern. Ledeglich wenn man ein Interface schreibt einmal in Lazarus STRG+SHIFT+G drücken um die GUID zu generieren, danach kann man die GUID direkt wieder vergessen.

Das ist hier auch nicht anders:
Code: Alles auswählen
 
 if t1.getinterface(itest,intf1) then begin
  intf1.test();
 end;
 

und die Tabellenabfrage geschieht nur einmal. Wenn man CORBA statt COM-Interfaces einsetzt gibt es zusätzlich keinen Overhead durch die Referenzzählung.
Der as Operator würde im notfall auch eine Exception werfen wenn die Klasse das Interface nicht implementiert. Daher kann man auch einfach mit einem try except arbeiten
Code: Alles auswählen
try
  ifVar := SomeObj as ISomeInterface
except
  on E: Exception do
    // kein ISomeInterface typ
end;

Hier ist die Performance noch schlechter. Verfolge mal mit dem Debugger was so alles passiert. Vor allem das Auslösen einer exception ist sehr aufwendig aber auch das einfache try/except ist nicht kostenlos. In MSElang verwende ich "Zero-cost exception handling" aber auch dort ist nur der exceptionlose Durchlauf durch die Exception-Strukturen ohne Zusatzaufwand und die EXE wird durch die abgelegten Verwaltungsinformationen grösser.
mse
 
Beiträge: 2013
Registriert: 16. Okt 2008, 09: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
Nach oben

Beitragvon MitjaStachowiak » 9. Dez 2018, 17:00 Re: Implementiert eine Komponente ein Interface?

So ne ganz dumme frage, warum verwendet man nicht einfach den is operator?


Hallo, hier ein Beispiel, was passiert, wenn man keine IDs für die Interfaces festgelegt hat:
Code: Alles auswählen
program interfaceproblem;
 
{$mode objfpc}{$H+}
 
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, sysutils;
 
type
  {$interfaces corba}
  IInt1 = interface
    procedure test1;
  end;
 
  IInt2 = interface
    procedure test2;
  end;
 
  TObj1 = class(IInt1)
    procedure test1;
  end;
 
  TObj2 = class(TObj1, IInt2)
    procedure test2;
  end;
 
procedure TObj1.test1;
begin
  writeln('test 1 called');
end;
 
procedure TObj2.test2;
begin
  writeln('test 2 called');
end;
 
procedure test;
var
  int : IInt1;
begin
  writeln('start test');
  int := TObj2.create as IInt1;
  int.test1// ERROR: prints test 2 called
end;
 
begin
  test;
end.
 


Ich hatte jüngst ein schwer auffindbares Bug in einem Programm mit diversen Interfaces. Die Is und As-Operatoren funktionierten zwar, aber der Aufruf der Interface-Methoden ging nicht so richtig. Dann habe ich entdeckt, dass ich die GUID vergessen hatte :roll:

Hier im Beispiel überschreibt die Implementierung von IInt2 offenbar irgendwie die Memory Tabelle von IInt1. Das geht auch, wenn die Methoden vollkommen verschiedene Parameter verwenden und dann sucht man den Fehler :mrgreen:

Wieso kompiliert der Code im Beispiel überhaupt ohne Fehlermeldung?
MitjaStachowiak
 
Beiträge: 340
Registriert: 15. Mai 2010, 12:46
CPU-Target: 64 bit
Nach oben

» Weitere Beiträge siehe nächste Seite »
Nächste

Zurück zu Freepascal



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 6 Gäste

porpoises-institution
accuracy-worried