DLl erzeugen

Für Fragen rund um die Ide und zum Debugger
mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: DLl erzeugen

Beitrag von mschnell »

pluto hat geschrieben:Wenn DLLS und SO Dateien verwenden werden sollen, sollen sie doch auch unter anderen Compilieren Funktionieren.
Dann müssen die Funktionen und callbacks "flach" sein (eben keine Klassen verwenden) und eine in allen Programmiersprachen definierbare Call-Konvention (ABI) verwenden. Also STDCALL oder CDECL.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: DLl erzeugen

Beitrag von mschnell »

Socke hat geschrieben:Inwiefern besteht eigentlich die Möglichkeit Methoden direkt als Flat-API zu verwenden?
Meinst Du Klassen-Methoden ? Gar nicht, weil die als zusätzlichen versteckten Parameter den Self-Pointer bekommen. Das kann das flache ABI ("Application Binary Interface") nicht. Deshalb muss ein flacher Wrapper verwendet werden.

-Michael

Socke
Lazarusforum e. V.
Beiträge: 3178
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: DLl erzeugen

Beitrag von Socke »

mschnell hat geschrieben:
Socke hat geschrieben:Inwiefern besteht eigentlich die Möglichkeit Methoden direkt als Flat-API zu verwenden?
Meinst Du Klassen-Methoden ? Gar nicht, weil die als zusätzlichen versteckten Parameter den Self-Pointer bekommen. Das kann das flache ABI ("Application Binary Interface") nicht. Deshalb muss ein flacher Wrapper verwendet werden.

-Michael
Den Self-Paramete Explizit zu übergeben ist für mich kein Problem. Ich hatte an sowas gedacht:

Code: Alles auswählen

type TGetIntFunc(Self Pointer): Integer = function;
 
function: dllfunc: TGetIntFunc;
  Result := TMethod(@TMyObject.GetInt).Code;
end;
 
procedure apfunc(handle: Pointer);
var
  f: TGetIntFunc;
begin
  f := dllfunc;
  f(handle);
end;
Meines Wissens nach ist ein Funktionszeiger auch nur ein Pointer. Also sollte das funktionieren.
Leider bietet Pascal keine Möglichkeit Methoden direkt als normale Funktionen zu exportieren.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: DLl erzeugen

Beitrag von Hitman »

mschnell hat geschrieben:
Hitman hat geschrieben:Exportieren kann man sie nicht und Referenzen auf sie sollte man auch nicht rausgeben - mangels einheitlichem Memory Manager und unterschiedlicher RTL.
Ich denke, Du kannst auch Klassen exportieren. Die ABI funktioniert natürlich nicht, wenn das Aufrufende Programm nicht auch ein FP-Programm ist. Die Klassen-Funktionen verwenden doch sicher den Memory-Manager, des Systems mit dem zusammen sie übersetzt sind.
Vorsicht - ich sagte "Klassen", nicht "Objekte". C++ z.B. kann Klassen exportieren, das macht sich QT ausgiebig zu Nutze. FreePascal kann das nicht, Delphi kann es über die BPLs.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: DLl erzeugen

Beitrag von mschnell »

Socke hat geschrieben:Meines Wissens nach ist ein Funktionszeiger auch nur ein Pointer. Also sollte das funktionieren. Leider bietet Pascal keine Möglichkeit Methoden direkt als normale Funktionen zu exportieren.
Es gibt zwei grundsätzlich verschiedene "Funktionszeiger"-Typen.

type fp procedure ....;

ist einfach nur ein Pointer auf den Code der Funktion

type fpc procedure of class;

ist ein Pointer auf den Code der Funktion plus eine Zeiger auf die Instanz ("self");

Der TMethod record spiegelt das ja wieder.

Deshalb ist es nicht möglich (und auch nicht sinnvoll), classen-Methoden als einfache Prozeduren zu exportieren. Ohne den Self-Pointer können sie nicht arbeiten. Und andere Programmiersprachen können sie nicht aufrufen, da das worauf self zeigt Implementierungs-Abhängig ist.

-Michael

Socke
Lazarusforum e. V.
Beiträge: 3178
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: DLl erzeugen

Beitrag von Socke »

mschnell hat geschrieben:Es gibt zwei grundsätzlich verschiedene "Funktionszeiger"-Typen.

type fp procedure ....;

ist einfach nur ein Pointer auf den Code der Funktion

type fpc procedure of class;

ist ein Pointer auf den Code der Funktion plus eine Zeiger auf die Instanz ("self");

Der TMethod record spiegelt das ja wieder.

Deshalb ist es nicht möglich (und auch nicht sinnvoll), classen-Methoden als einfache Prozeduren zu exportieren. Ohne den Self-Pointer können sie nicht arbeiten. Und andere Programmiersprachen können sie nicht aufrufen, da das worauf self zeigt Implementierungs-Abhängig ist.
Der letzte Teil widerspricht doch dem vorhergegangenen.
1. type fpc = procedure; ist ein einfacher Zeiger auf die Adresse des Codes.
2. type fpc = procedure of object; (of class ist nicht möglich) ist ein TMethod-Record. TMethod enthält die beiden Elemente Code und Data, jeweils ganz normale, untypisierte Pointer. Code ist ein einfacher Funktionszeiger; ohne passende Argumente aufgerufen ist man aber sehr nahe an einer AccessViolation. Data ist ein Zeiger auf das Objekt. Was wäre jetzt falsch daran type fpc = procedure of object; mit type fpc = procedure(Self: TObject); gleichzusetzen (war das nicht der Weg, wie man eine prozedurale Sprache durch Klassen erweitert)?

Hitman hat geschrieben:Vorsicht - ich sagte "Klassen", nicht "Objekte". C++ z.B. kann Klassen exportieren, das macht sich QT ausgiebig zu Nutze. FreePascal kann das nicht, Delphi kann es über die BPLs.
Das Klassenkonzept von C++ ist mir nicht ausreichend bekannt um zu beurteilen, wie Klassen exportiert werden. Ein wenig vom OOP-Verständnis von Klassen, Objekten und Instanzen abstrahiert, sehe ich Klassen als eine Sammlung von Funktionen und Prozeduren an, die auf eine bestimmte Datenstruktur angewendet werden können.
Die sprachabhänige Implementation dieses OOP-Konzeptes erleichtert nur ein wenig die Handhabung, sodass man nicht die Datenstruktur als Parameter angibt. Stattdessen werden die Methoden als Bestandteil einer Instanz geschrieben (myvar.myproc), womit suggeriert wird, dass sie zu jeder Instanz individuell existieren. Da die VMT aber von der Klasse erstellt wird (und nicht von jeder Instanz selbst) und die Methoden einer Klasse auch nur ein einziges Mal im Speicher liegen, wird im Endeffekt nur eine Funktion einer Klasse (das ist keine Class-Procedure!) aufgerufen, die eine bestimmte Instanz der Klasse bearbeitet.
-- soviel zu Klassen und Objekten, jetzt zum Exportieren.
Exportieren bedeutet, dass interne Ressourcen (was das auch immer konkret sein soll) nach außen hin verfügbar gemacht werden. So könnte man auch das Klassen-Keyword public in gewissem Sinne als "Export" bezeichnen.
Wenn also ein Objekt exportiert wird, heißt das: dieses Objekt kann von außen benutzt werden. Wie dieses Objekt benutzt wird, ist aber sprach- und schnittstellenabhängig. Eine klassische flat-API nimmt als Parameter einen Zeiger entgegen, castet diesen in ein Objekt und ruft eine entsprechende Methode auf.
Eine Klasse definiert, wie sich alle Instanzen (Objekte) dieser Klasse verhalten. Wird eine Klasse exportiert, ist dem Importeur die Speicherverwaltung einer Instanz (was wo abgelegt) und die Implementation (was wo ausgeführt wird) bekannt. Ein solches Konzept wird von FreePascal nicht unterstützt. Die Implementation bestimmter Methoden kann mit Interfaces "exportiert" werden. Hier ist aber zu sagen, dass kein echter Export durchgeführt wird. Es wird lediglich ein Pointer (bzw. ein ganzer Record; genaueres weiß ich nicht) zur Verfügung gestellt, der sprachübergreifend standardisiert ist.

Meine Aussage, dass man durch einfaches Abflachen der Methoden Klassen exportieren könne, ist somit falsch! Ich möchte also weder Klassen noch Instanzen einer Klasse oder Objekte exportieren sondern bestimmte Methoden einer Klasse als flache API ansprechen.

Edit: Zumindest innerhalb eines Programm geht alles wunderbar:

Code: Alles auswählen

program methodcall;
{$mode objfpc}{$H+}
 
type
  TMyObject = class(TObject)
    FInt: Integer;
    procedure SetInt(const AValue: Integer);
  end;
  TSetIntProc = procedure(Self: TMyObject; const AValue: Integer);
 
procedure TMyObject.SetInt(const AValue: Integer);
begin
  FInt := AValue;
end;
 
var
  o: TMyObject;
  m: TMethod;
begin
  o := TMyObject.Create;
  m := TMethod(@o.SetInt);
  Writeln(o.FInt);
  TSetIntProc(m.Code)(TMyObject(m.Data),5);
  Writeln(o.FInt);
  o.Free;
end.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: DLl erzeugen

Beitrag von mschnell »

Richtig. Nützt aber nix.

Wenn die DLL und das aufrufende Programm in FPC ist, kannst Du auch gleich einfach normale objektorientierte Funktionsaufrufe benutzen, weil beide Seiten wissen, wie sie damit umgehen müssen (ABI, hidden self parameter, ...).

Wenn z.B. die DLL in C++ und das aufrufende Programm in FPC ist oder umgekehrt, musst Du "flache" Aufrufe mit STDCall oder CDELC verwenden, damit das ABI passt. Wenn Du dann aber self Pointer mithilfe von TMethod übergibst, kann die andere Seite nix damit tun, weil dort die Instanz-Strukturen anders definiert sind.

-Michael

Antworten