AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung
Thema durchsuchen
Ansicht
Themen-Optionen

Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

Ein Thema von Zacherl · begonnen am 9. Sep 2017 · letzter Beitrag vom 11. Sep 2017
Antwort Antwort
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#1

Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 9. Sep 2017, 14:09
Delphi-Version: 10 Berlin
Hallo zusammen,

ich erstelle grade Delphi Header für eine von mir in C geschriebene Library. Die technische Umsetzung ist kein Problem, allerdings stellen sich mir einige Fragen bezüglich des Code-Designs / der Organisation der Klassen:
  • Was bevorzugt ihr persönlich bezüglich der Kapselung?
    1. Die Low-Level C-API komplett in Delphi Klassen kapseln, um möglichst OOP konform zu sein und bestmöglich alle Delphi Sprachfeatures auszunutzen
    2. Die Low-Level C-API 1 zu 1 übersetzen und dann zusätzlich noch eine Unit mit Wrapper-Klassen erstellen (macht ZLibEx z.b. so)

Dann zur Aufteilung der Klassen. Die C-Lib hat 3 große Module (nennen wir sie A, B, C) und jedes Modul beinhaltet mehrere Typen und Klassen, welche ich in unterschiedliche Header-Files aufgeteilt habe. In C ist das kein Problem, da ich alle diese Header in einem "Main-Header" zusammenfassen kann, so dass der User letztlich nur diese eine Datei inkludieren muss. Bei Delphi ist das natürlich anders.
  • Was ist eure persönliche Präferenz im Bezug auf Aufteilung von Klassen auf Units?
    1. Möglichst nah an der C-Lib halten und Typen/Klassen logisch auf verschiedene Units verteilen (der User muss dann bei Verwendung diverse Units mit in uses aufnehmen)
    2. Alles in eine einzelne Unit packen (bzw. höchstens API und Wrapperklassen splitten)
    3. Nach Modulen auf einzelne Units verteilen (wobei dann trotzdem noch 1-2 extra Units inkludiert werden müssen, welche gemeinsame Typen enthalten)

Lasst mal eure Meinung hören!

Viele Grüße
Zacherl
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl ( 9. Sep 2017 um 18:49 Uhr)
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#2

AW: Delphi Header für C-Lib - Umsetzung

  Alt 9. Sep 2017, 18:07
zu 1:
- fände es gut, wenn die LowLevel Umsetzung sich möglichst nah am 1:1 Prinzip orientiert, denn:
- die CLIB Sachen setzt entweder der ein, der sie von C "so" kennt
- oder jemand ohne viel C-Erfahrung portiert einen C-Source wo ja auch die Urform verwendet wird
- wer in Delphi eine "optische" Kapselung will, kann ja den NameSpace stets mit davor schreiben... ala clib.atoi('123')
- eine Delphi OPP Schicht zur unterstützung von SprachFeatures kann für Step 2 und Leute mit Delphi&C Erfahrung nicht schaden, ist aber dann nicht mehr rückwärtsprotabel, ausser es man erstellt gleiches auch als logisch kompatibel gleich mit in CPP


zu 2:
- für pure 1:1 Portierung wäre es im Prinzip egal, weil wer C Quellen 1:1 umsetzt, sieht dort ja die "Header includes", kaönnte die also 1:1 als "Delphi uses" übernehmen, ABER in C ist die Reihenfolge fast wurcsht und man hat dort oft mehr include als nötig, denn es wird zur Vermeidung von "mehrfachen Defines/Typen/..." einfach in den Headern stets gefragt, ob das Defines des HeaderFiles noch nicht definiert is, dann wird es "einmal" definiert und alles nötige gamacht. Bei den nächsten 100 includes passiert dann nix mehr.. das ist in C & CPP ja so üblich, da streikt aber das Konzept der DelphiUses im InterfaceTeil und kann nur per Trick und viel Mühe im Einzelfall durch "ImplemenationUses" von Kreuzreferenzen befreit werden...

-> also besser alles LowLevel in EINE CLib unit (auch wenn das den DelphiLinker dazu verleitet die EXE mit vielen wohl ungenutzten Sachen etwas aufzublähen.
-> ein CLIB.INC Files, welches im InterfaceTeil bei uses einfach per include eingebunden wird, hält die Option offen, es später doch in mehrere .pas Units aufzuteilen, da aber nur C Kenner wissen, was sich in welcher UNIT/Header, wäre dies Option mehr der Ansatz für C Spezialisten, sich hier ihre eigenen Sachen "zentral" mit einzu binden


CLIB4PAS klingt definitiv interessant!
Hatte vor Jahren mal für 100T Pas-VCL-CodeZeilen sowas rückwärts gemacht, also quasi ne echte CPP VCL gemäß der CPP Header vom C++Builder relalisiert, damit man auch in VC z.B. eine VCL Stringlist, TList,... oder einen VCL (C)AnsiString hatte... das hat mir damals bei der schnellen ersten Version einer PAS->CPP Portierung eines guten PAS BackEnd Frameworks sehr geholfen.
Den so generalisierten Ansatz wie du jetzt hier planst habe ich selbst nie gemacht, beim manuellen C->PAS Convert nutze ich nur eine eigene PAS unit, wo ich eben all die Dinge über die zeit so rein genommen haben, welche ich in meinen C Quellen eben so verwende
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: Delphi Header für C-Lib - Umsetzung

  Alt 9. Sep 2017, 18:47
Hey hey, erstmal danke für deine Antwort!

Ich glaube allerdings wir reden ein wenig aneinander vorbei Meine Frage bezieht sich auf eine ganz konkrete Library (die ich in C geschrieben habe) und nicht die C-Runtime Lib (habe mal versucht den Titel etwas eindeutiger zu gestalten). Einige Ansätze aus deiner Antwort kann ich allerdings trotzdem übernehmen. Das Verwenden von .inc Files ist auf jeden Fall eine gute Idee, mit der ich mein C-Header-Konzept beibehalten könnte, trotzdem aber auf Delphi Seite nur eine Unit hätte.

Vermutlich werde ich so vorgehen, dass ich eine Low Level Implementierung unter dem Namen MyLib.API.pas oder einfach nur MyLib.pas erstelle, welche die kompletten Funktionen und Datentypen der C-Lib bereitstellt. Zusätzlich kann ich dann noch die Units MyLib.ModuleA.pas , MyLib.ModuleB.pas und MyLib.ModuleC.pas erstellen, welche dann jeweils die Kapselung der 3 Module in richtigen (Wrapper-)Klassen bereitstellt.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#4

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 9. Sep 2017, 21:18
Jupp, im Prinzip stimme ich mit dem Überein, was schon mein Vorredner sagte.


Theoretisch kann man Methden von Klassen auch direkt external mit Prozeduren verlinken,

aber in der Prakis hat es sich bewährt erstmal die API möglichst 1:1 nach Pascal zu übersetzen,
somit kann diese Schnittstelle erstmal genauso verwendet werden, wie es die Dokumentation und die vielen C-Beispiele zeigen (wenn man eine bekannte C-Lib übersetzt).
Allerdings passe ich die Namen der Parameter öfters mal an (mehr Delphi-Style und entferne Prefixe).
Auch Typen passe ich dem Delphi-Style an, so lange es möglich ist, wie z.B. Integer statt INT oder VAR statt Pointer.
Sogar Const-String statt PChar ist oftmals möglich, bei IN-Parametern, aber lohnt sich nur, wenn sowas bei allen Prozeduren möglich ist, da mit sich möglichst alles an einen "einheitlichen" Style hält.

Wenn direkt als Klasse, dann aber möglichst nah an der originalen API. (Benamung)

Wie gesagt, so kann man die API "auch" im Original-Style verwenden
und vorallem ist es so einfacher später neue Verionen der API einfließen zu lassen.



Falls ich Zeit hab, dann würde ich das Ganze dann nochmal in "schönere" Klassen/Interfaces mit "verständlicheren" Methodennamen kapseln (Wrapper),
bzw. kleinere spezialisiertere, minimalistische und einfache Komponenten.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu ( 9. Sep 2017 um 21:28 Uhr)
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.075 Beiträge
 
Delphi 12 Athens
 
#5

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 10. Sep 2017, 07:22
Ich würde es auch eher 1:1 umsetzen, allein schon wegen der Wartbarkeit und lesbarkeit.

Allerhöchstens wenn A, B, C trivial sind würde ich das in eine Unit nehmen, aber das wäre meiner Meinung nach etwas schlechter Stil, weil A, B, C sicherlich auch verschiedene Aufgaben haben.
Diese Aufgabentrennung sollte man am Besten auch in Delphi 1:1 abbilden.

Rollo
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#6

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 10. Sep 2017, 16:10
in der Prakis hat es sich bewährt erstmal die API möglichst 1:1 nach Pascal zu übersetzen
Alles klar

Allerdings passe ich die Namen der Parameter öfters mal an (mehr Delphi-Style und entferne Prefixe).
Auch Typen passe ich dem Delphi-Style an, so lange es möglich ist, wie z.B. Integer statt INT oder VAR statt Pointer.
Perfekt. Das habe ich auch bereits so gemacht. Typen mit T geprefixt, CamelCase Schreibweise und var bzw. const für Pointer. Zusätzlich habe ich für einige Enums noch Helper Methoden wie z.b. ToString implementiert (dafür hat die C-Lib jeweils eine exportierte Funktion).

Ich würde es auch eher 1:1 umsetzen, allein schon wegen der Wartbarkeit und lesbarkeit.

Allerhöchstens wenn A, B, C trivial sind würde ich das in eine Unit nehmen, aber das wäre meiner Meinung nach etwas schlechter Stil, weil A, B, C sicherlich auch verschiedene Aufgaben haben.
Diese Aufgabentrennung sollte man am Besten auch in Delphi 1:1 abbilden.
Ich werde jetzt wohl den Kompromiss wählen und für jeden C-Header eine .inc Datei erstellen, die ich dann später in einer einzelnen API Unit zusammenfasse. Dann habe ich die Sachen ordentlich getrennt und man muss hinterher trotzdem nich 15 (Sub-)Units einbinden, damit alle Typen der Lib zur Verfügung stehen. Für die Kapselung in Klassen erstelle ich dann nochmal 3 weitere Units mit den entsprechenden Modulen A, B und C. Dann muss der User unter Delphi immer die API Unit verwenden und je nach gewünschtem Modulen noch die entsprechende Unit mit den Wrapperklassen einbinden.

Danke euch allen!

Noch irgendwelche Tipps zum Lösen von Namenskonflikten die auftreten, wenn ich zusätzliche Wrapperklassen schreibe? Habe in C ja meine Context-Structs wie z.b. MyDecoder , die ich dann erstmal 1:1 nach Delphi als TMyDecoder = record umsetze. Die Wrapperklasse kann ich jetzt natürlich nicht auch TMyDecoder nennen. Hier lieben den Namen der Wrapperklasse mutieren (TMyDecoderWrapper oder sowas) statt Den des Records? Oder doch liebe den Record umbenennen in z.b. TMyDecoderStruct und dafür die High-Level Klasse bei TMyDecoder belassen?

Achso und würdet ihr Klassen, Typen, Records, Konstanten, etc. mit dem Namen der Lib prefixen? Mangels Namespaces mache ich das eigentlich ganz gerne in Delphi (auch wenn man Mehrdeutigkeiten natürlich mit explizitem Angeben der Unit lösen kann - was aber auch irgendwie nichts Ganzes und Halbes ist).
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#7

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 10. Sep 2017, 17:22
Zur Namensgebung und als Alternative, wo man in C das simpel per Groß/Klein-Schreibung macht:

TMyTool verwende ich in Delphi nur für "echte" Klassen/Objekte, also alles was man per CreateAufruf erzeugen muss
RMyTool verwende ich in Delphi für Records, also C Structs, welche sich auch statisch ohne "Create" nutzen lassen
PMyTool definiere ich immer zusammen mit RMyTool als typisierten Pointer von RMyTool

Mit Operatoren und Propertys für Records also "R..." habe seit D2007 sehr gute Erfahrungen mit allen weiteren Delphi Varianten.
Ich nutze insbesondere die "default" Property oft als typisiertes virtual Array/List... funktioniert seit D2007 super und ersetzt mit die doofen TList<XY> Generics die zwar schön einfach zu tippen sind, aber bei mir schlicht zu langsam und zu instabil sind.


Präfixe:
- auch wenn OldScool: ich mache es auch heute noch
- allgemeines wie ein GetValue, ein UpdateStorage oder UpdateGUI sind aus meiner Sicht "ohne Kontext" sehr schlecht im Code zu verstehen... daher lieber mylibGetValue, mylibUpdateXXX
- ich ziehe das trotz der paar Zeichen Zusatztiparbeit so durch, hat den positiven neben Effekt, das fremde in der Anwendung auch stets sofort sehen&wissen in woher das kommt und bei bedarf dort finden, wozu das ist und was es macht

Wer nur mit allgemeingültigen Interfaces ala "WriteData" arbeiten will, der soll es tun und kann es sich selbst HighLevel so definieren. Ich bevorzuge bei LowLevel und ExternalModule einer kurze 1:1 PräfixVariante, welche bei mir auch so schon im C Source vorhanden ist

Geändert von mensch72 (10. Sep 2017 um 17:35 Uhr)
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 11. Sep 2017, 07:18
Zu a:

Ich würde sie erstmal 1 zu 1 umsetzen und dann darauf aufbauend eine entsprechende Klassenhirarchie aufbauen.

Eine Funktions-Lib in eine Klassenhirarchie zu packen, nur damits OOP-Konform ist, macht keinen Sinn und erzeugt i.d.R. nur Overhead.


Zu b:

Hier würd ich zu 1. tendieren. Damit erleichtert man die Orientierung, sowohl im Source, als auch ggf. in der Dokumentation.
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#9

AW: Delphi Unit (Language Binding) für eigene C-Lib - Umsetzung

  Alt 11. Sep 2017, 17:28
TMyTool verwende ich in Delphi nur für "echte" Klassen/Objekte, also alles was man per CreateAufruf erzeugen muss
RMyTool verwende ich in Delphi für Records, also C Structs, welche sich auch statisch ohne "Create" nutzen lassen
PMyTool definiere ich immer zusammen mit RMyTool als typisierten Pointer von RMyTool
Ansich eine gute Konvention, allerdings nicht ganz konform mit dem Pascal Style Guide, welcher das T Prefix für sämtliche Typen vorsieht. Allerdings sehe ich hier zumindest bezüglich der CamelCase Bezeichner sogar explizit eine Ausnahme für Header Translations. Muss ich mich mal umsehen, wie die breite Masse das hier so handhabt. In den JEDI API Headern meine ich mich erinnern zu können, dass Typen immer erst mit ihrem originalen Namen (ohne T Prefix und vollständig in Capslock) deklariert wurden und danach nochmal ein Alias mit T Prefix und CamelCase angelegt wurde.

Präfixe: - auch wenn OldScool: ich mache es auch heute noch
Schön zu sehen, dass ich hier nicht alleine stehe

Ich würde sie erstmal 1 zu 1 umsetzen und dann darauf aufbauend eine entsprechende Klassenhirarchie aufbauen.

Eine Funktions-Lib in eine Klassenhirarchie zu packen, nur damits OOP-Konform ist, macht keinen Sinn und erzeugt i.d.R. nur Overhead.
Habe jetzt mal mit der 1:1 Übersetzung angefangen. Die Sache mit den *.inc Files ist leider doch nicht so wirklich praktikabel, weshalb ich die 1:1 Übersetzung vorerst komplett in eine Unit packe. Bezüglich der Funktions-Libs hast du recht. In meinem Falle beziehen sich die Funktionen aber immer auf eines der der 3 Module (ModuleAInit, ModuleAMachWas, ModuleASetThis, ModuleAGetThat , etc), sind also perfekt in Klassen zusammenfassbar.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:59 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz