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.