Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Fr Nov 01, 2024 02:30

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Verbindung FrontEnd => Backend
BeitragVerfasst: Fr Jul 18, 2014 21:06 
Offline
DGL Member
Benutzeravatar

Registriert: Di Okt 03, 2006 14:07
Beiträge: 1277
Wohnort: Wien
Einen guten Abend wünsche ich Euch

und habe nach langer Zeit wieder mal eine Frage (Achtung! FreePascal):

Zunächst mal: mit dem obigen "FrontEnd" ist keine GUI gemeint, sondern eine Klasse, die als Container für etliche andere "Helfer"-Klassen ("BackEnds") eingesetzt wird und gleichzeitig eine schöne übersichtliche Programmier-Schnittstelle zur Verfügung stellen soll. Die BackEnds sind für die eigentliche Verarbeitung zuständig.

Voraussetzung: die Programmier-Schnittstelle sollte schnell und sicher sein.



Ursprünglich - weil mir auch damals schon nichts Besseres eingefallen ist - habe ich das mittels Object-Properties implementiert, wobei der Programmierer hier auf alle öffentlichen Properties/Methoden des jeweiligen BackEnds zugreifen kann:

Code:
  1. TBackEnd = Class;
  2.      
  3.      TFrontEnd = Class(TObject)
  4.      Protected
  5.         fBackEnd: TBackEnd;
  6.      Public
  7.         Property BackEnd: TBackEnd Read fBackEnd;
  8.      End; 


Das ist schnell, aber hier kann der Programmierer auch auf TBackEnd.Destroy zugreifen, womit man die BackEnds einfach löschen kann. Für eine API ist das meiner Meinung nach ein Disaster.

Weiters habe ich Property-Interfaces ausprobiert, mit dem gleichen Ergebnis wie vorher.

Ich habe mal vor längerer Zeit im Lazarusforum drüben nachgefragt, aber die Antwort war: "Das ist eben so. Kann man nichts machen."



Als Alternative gibt es folgende Standardversion:
Der Programmierer ruft "Frontend.DoSomething" auf, welches wiederum "Backend.DoSomething" aufruft.
Das ist sicher, aber nicht besonders schnell: zwei Methoden-Aufrufe hintereinander :(



Mein letzter Versuch benutzt einen Delegaten, dabei ruft die Methode "Frontend.DoSomething" zwar auch die Methode "Backend.DoSomething" auf, aber diesmal direkt über dessen Methodenzeiger, wobei ich hoffe, das das etwas flotter ist.

Google war diesmal auch keine große Hilfe.


Meine Frage lautet jetzt: Ist die Sache mit dem Methodenzeiger schon das Schnellste, was ich kriegen kann, oder habt Ihr noch bessere Ideen?


Viele Grüße,
Traude


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Fr Jul 18, 2014 22:10 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Erstmal die obligatorische Frage: Hast du geprofiled und rausgefunden, dass ein doppelter Methodenaufrauf tatsächlich ein Problem ist? Das sollte ein bis zwei Fetches (zwei bei virtuellen Methoden) und ein Call sein, wenn die Argumente in der gleichen Reihenfolge bleiben und du auf 64bit bist (auf 32bit wird noch nen bisschen Stack dazugebaut, um die Argumente zu duplizieren; bei 64bit ab einer gewissen Anzahl auch).

Dann hab ich noch nicht ganz raus was dein Problem mit Interfaces ist. Wenn du das Reference-Counting aushebelst (kannst du in deiner Implementation von IUnknown machen) sollte das sicher sein, solange du den Destruktor nicht mit ins Interface packst. Der Vorteil bei Interfaces ist, dass sie schnell zu bauen sind. Geschwindigkeitstechnisch müssten sie in einer Größenordnung mit dem Methodenzeiger liegen.

Wenn Interfaces keine Option sind, Methodenzeiger, ja. Über Properties gegen Schreibzugriff absichern und dann sollte das hinhauen.

viele Grüße,
Horazont

P.S.: Darf man fragen, wofür das ist? Ich mache inzwischen relativ viel Python, und da geht man eher so dran, dass man APIs nicht versteckt, sondern nicht dokumentiert, wenn sie nicht verwendet werden sollen, und schreibt dem Benutzer einen gewissen Common Sense zu.

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Fr Jul 18, 2014 23:54 
Offline
DGL Member
Benutzeravatar

Registriert: Di Okt 03, 2006 14:07
Beiträge: 1277
Wohnort: Wien
Lord Horazont hat geschrieben:
Erstmal die obligatorische Frage: Hast du geprofiled und rausgefunden, dass ein doppelter Methodenaufrauf tatsächlich ein Problem ist? Das sollte ein bis zwei Fetches (zwei bei virtuellen Methoden) und ein Call sein, wenn die Argumente in der gleichen Reihenfolge bleiben und du auf 64bit bist (auf 32bit wird noch nen bisschen Stack dazugebaut, um die Argumente zu duplizieren; bei 64bit ab einer gewissen Anzahl auch).

Nein, geprofiled habe ich noch nicht. Ich wollte bloß wissen, ob ich irgend etwas Offensichtliches übersehen habe.
Ich bin immer noch auf 32 Bit, vermutlich weil ich aus einer Zeit stamme, wo die Leute mit 64k RAM auskommen mussten und ich mich ärgere, was heutzutage für Platz verschwendet wird.

Lord Horazont hat geschrieben:
Dann hab ich noch nicht ganz raus was dein Problem mit Interfaces ist.

Ich habe mir einfach mein FreePascal-LanguageReference-Handbuch hergenommen und eine kleine Probeanwendung erstellt:

Code:
  1. [
  2. {$interfaces corba}
  3. type
  4.     IMyInterface = interface
  5.     procedure P1;
  6.     end;
  7.  
  8.     TDelegateClass = class(TObject, IMyInterface)
  9.     private
  10.         procedure P1;
  11.     end;
  12.  
  13.     TMyClass = class(TInterfacedObject, IMyInterface)
  14.     private
  15.         FMyInterface: TDelegateClass; // class type
  16.    public
  17.         property MyInterface: TDelegateClass
  18.         read FMyInterface implements IMyInterface;
  19.     end;
  20.  


Und weil das "FMyInterface" wie man oben schön sehen kann, ja nichts anderes ist als das Objekt selbst, wird bei einem Call: TMyClass.MyInterface.Free genauso das SubObjekt freigegeben, als wenn ich gleich ein ganz normales Object Property implementiere. Zwischen Object Property und Interface Property ist *kein* Unterschied, bei beiden kann das implementierende Objekt einfach gelöscht werden. Und genau das will ich ja vermeiden.

Bei Corba-Objekten braucht man das Interface nicht aushebeln, die haben gar keine Referenz-Zählung, lt. Pascal-Handbuch gibt es die nur bei MSWindows COM-Objekten.

Wenn die Interfaces dem Methodenzeiger ungefähr gleich sind, wärs mir recht, denn dann kann ich dabei bleiben. Ich werde aber das Profiling noch nachholen.

Meine derzeitige Lösung sieht so aus:

Code:
  1.  
  2. Type
  3.    TPrintProc = Procedure(AFontIndex: LongWord;
  4.                    AString: WideString) Of Object;
  5. //********************************************************************
  6.    TBackEnd = Class(TObject)
  7.    Public
  8.         Procedure Print(AFontIdx: LongWord; AStr: WideString);
  9.    End;
  10. //********************************************************************
  11.    TCustomFrontEnd = Class(TObject)
  12.    Protected
  13.       fPrintProc: TPrintProc;
  14.    Public
  15.       Constructor Create; Reintroduce;
  16.        Destructor Destroy; Override;
  17.         Procedure DoBackEndPrint(AFontIdx: LongWord; AStr: WideString);
  18.    End;
  19.  
  20. //********************************************************************
  21. //********************************************************************
  22. IMPLEMENTATION
  23. //********************************************************************
  24. //********************************************************************
  25. //   TBACK_END
  26. //********************************************************************
  27. Procedure TBackEnd.Print(AFontIdx: LongWord; AStr: WideString);
  28. Begin
  29.    // RealPrint
  30. End;
  31. //********************************************************************
  32. //********************************************************************
  33. //   TCUSTOM_FRONT_END
  34. //********************************************************************
  35. Constructor TCustomFrontEnd.Create;
  36. Begin
  37.    Inherited Create;
  38.    fBackEnd:= TBackEnd.Create;
  39.    fPrintProc:= fBackEnd.Print;
  40. End;
  41. //********************************************************************
  42. Destructor TCustomFrontEnd.Destroy;
  43. Begin
  44.    fBackend.Free;
  45.    Inherited Destroy;
  46. End;
  47. //********************************************************************
  48. Procedure TCustomFrontEnd.DoBackEndPrint
  49.                    (AFontIdx: LongWord; AStr: WideString);
  50. Begin
  51.    fPrintProc(AFontIdx,AStr);  // Direct Access
  52. End;
  53. //********************************************************************
  54. End.
  55.  



Übrigens, "TBackEnd.Print" heißt im Original "TFontManager.Print"

Vielen Dank für Deine zweite Meinung,
und viele Grüße,
Traude


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Sa Jul 19, 2014 08:26 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Hm, Interfaces haben ein Free? Dann ergibts natürlich Sinn, das wusste ich (noch) nicht.

Mit deinen Methodenzeigern hast du wieder zwei Calls, da gewinnst du nichts. Ich dachte du würdest die per Property rausreichen:
Code:
  1. Type
  2.    TPrintProc = Procedure(AFontIndex: LongWord;
  3.                    AString: WideString) Of Object;
  4. //********************************************************************
  5.    TBackEnd = Class(TObject)
  6.    Public
  7.         Procedure Print(AFontIdx: LongWord; AStr: WideString);
  8.    End;
  9. //********************************************************************
  10.    TCustomFrontEnd = Class(TObject)
  11.    Protected
  12.       fPrintProc: TPrintProc;
  13.    Public
  14.       Constructor Create; Reintroduce;
  15.       Destructor Destroy; Override;
  16.    Public
  17.       property DoBackEndPrint: TPrintProc read fPrintProc;
  18.    End;


viele Grüße,
Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Sa Jul 19, 2014 09:53 
Offline
DGL Member
Benutzeravatar

Registriert: Di Okt 03, 2006 14:07
Beiträge: 1277
Wohnort: Wien
Lord Horazont hat geschrieben:
Hm, Interfaces haben ein Free? Dann ergibts natürlich Sinn, das wusste ich (noch) nicht.
Nein, Interfaces haben kein Free, aber das Objekt, welches das Interface implementiert, hat ein Free:
Code:
  1.        
  2.        property MyInterface: TDelegateClass
  3.            read FMyInterface implements IMyInterface;
  4.  
Das Ding, das hier "MyInterface" genannt wird, ist eine normale Pascal Klasse und zur Laufzeit also ein Objekt und kann über das Property freigegeben werden.


Lord Horazont hat geschrieben:
Mit deinen Methodenzeigern hast du wieder zwei Calls, da gewinnst du nichts. Ich dachte du würdest die per Property rausreichen:
Code:
  1. Type
  2.    TPrintProc = Procedure(AFontIndex: LongWord;
  3.                    AString: WideString) Of Object;
  4. //********************************************************************
  5.    TBackEnd = Class(TObject)
  6.    Public
  7.         Procedure Print(AFontIdx: LongWord; AStr: WideString);
  8.    End;
  9. //********************************************************************
  10.    TCustomFrontEnd = Class(TObject)
  11.    Protected
  12.       fPrintProc: TPrintProc;
  13.    Public
  14.       Constructor Create; Reintroduce;
  15.       Destructor Destroy; Override;
  16.    Public
  17.       property DoBackEndPrint: TPrintProc read fPrintProc;
  18.    End;


Nein, das möchte ich nicht, weil dazu folgender Vorgang nötig wäre:

Das FrontEnd müsste in seinem Konstruktor den leeren **öffentlichen** Methodenzeiger mit der Methode "TBackEnd.Print" bestücken. Dann ist es möglich, dass der Programmierer das direkt aufrufen kann. Verstehst Du, einen öffentlichen nackten Methodenzeiger. Das wäre zwar besser, als ein nacktes Subobjekt (wie ich es jetzt habe), aber für eine ordentliche API finde ich das nicht tragbar.

Da bleibt mir wohl nichts übrig als der zweifache Methodenaufruf.

Das ist schade, denn es handelt sich um das Fontend einer, naja, nicht mehr ganz so kleinen Grafik-Engine für OpenGL 3.3. Das ganze war ursprünglich gedacht für die GUI, ist aber total getrennt von ihr und immer weiter gewachsen. Seit ungefähr 2009 beschäftige ich mich damit, und die Version 1.0 hat sie jetzt endlich erreicht. Die Print-Methode habe ich als Beispiel herausgenommen, es gibt noch etliche andere Methoden, die genauso gehandhabt werden. Diese Methoden sammeln die Daten für den nächsten Rendervorgang und es wäre schön, wenn das so schnell wie möglich wäre. Aber im Augenblick ist die Sicherheit (und Lesbarkeit) wichtiger, weil es das eben das Canvas-Container-Objekt ist, das Frontend nach außen.




Nachtrag: Hab's hingekriegt :wink: , letztendlich mit Corba-Interfaces und einigem Herumprobieren, weil das FPC-Handbuch bei diesem Thema irgendwie auslässt.

Danke jedenfalls
Traude


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Mo Jul 21, 2014 20:45 
Offline
Guitar Hero
Benutzeravatar

Registriert: Do Sep 25, 2003 15:56
Beiträge: 7804
Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Über Methodencalls mach ich mir - insbesondere bei Frameworks - keine Gedanken. Es ist viel wichtiger Lesbaren und wiederverwendbaren Code zu produzieren. Wenn es dazu nötig ist 3 Ebenen zu routen, dann ist das eben so.

Das Thema fällt unter "Pre Mature Optimazation" und "YAGNI - You Aint Gonne Need It".

_________________
Blog: kevin-fleischer.de und fbaingermany.com


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Verbindung FrontEnd => Backend
BeitragVerfasst: Di Jul 22, 2014 08:19 
Offline
DGL Member
Benutzeravatar

Registriert: Di Okt 03, 2006 14:07
Beiträge: 1277
Wohnort: Wien
Am besten ist es natürlich, wenn man schnellen UND lesbaren Code produziert. :wink:


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

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


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.011s | 16 Queries | GZIP : On ]