Es ist nicht Delphi, sondern Windows, bzw. das
PE-Dateiformat (COFF).
Ja, statische
DLL-Importe laufen über eine Import-Tabelle, welche aus vielen JMP+Adresse besteht,
wo Delphi erstmal mit einem CALL rein springt und von dort dann erst zum eigentlichen
DLL-Export.
Und ja, wenn ich diese Adressen selbst verwalte (statt Methoden also MethodenZeiger verwende), könnte ich jeweils den einen JMP weglassen.
Aber erstmal ist die Verwendung solcher Interfaces unintuitiver und ich habe eh noch Hoffnung auf bessere Lösungsansätze.
* Direkt den originalen Quellcode neu kompilieren.
* Oder zumindestens statt der .DCU an .OBJ-Dateien zu gelangen und statt der
DLL alles direkt in die EXE zu linken.
Zukünftig ist noch geplant die Einzel-Funktionen und Interfaces in Custom-Records zu verpacken.
Dann würde ich da eventuell weitere Überlegungen in Betracht ziehen, dort nochmal etwas zu optimieren, bezüglich der JUMP/CALL.
Mir ging es erstmal darum zusätzlich noch weitere/eigene JUMP/CALL dort reinzuschieben,
und auch dafür noch tausende Wrapper-Funktionen schreiben zu müssen.
(das hat sich zum Glück nun erledigt)
Prinzipiell kompiliert jetzt erstmal alles,
auch wenn ich an einigen Stellen noch rumfummeln muß. (aktuell sind dynamische Arrays und Strings dran)
Delphi 7
DLL-Exports :
/Part_II/DECMath.dpr
Delphi 2006+ Imports :
/Part_II/DECMath.pas
Zitat:
Delphi-Quellcode:
procedure NSet(var A: IInteger; B: Integer); overload;
procedure NSet(var A: IInteger; B: Int64); overload;
procedure NSet(var A: IInteger; B: Extended); overload;
procedure NSet(var A: IInteger; const B: IInteger = nil; Abs: Boolean = False); overload;
procedure NAdd(var A: IInteger; B: Integer); overload;
procedure NAdd(var A: IInteger; const B: IInteger); overload;
procedure NSub(var A: IInteger; B: Integer); overload;
procedure NSub(var A: IInteger; const B: IInteger); overload;
procedure NMul(var A: IInteger; B: Integer); overload;
procedure NMul(var A: IInteger; B: Int64); overload;
procedure NMul(var A: IInteger; const B: IInteger; C: Int64); overload;
procedure NMul(var A: IInteger; const B: IInteger; C: Integer); overload;
procedure NMul(var A: IInteger; const B: IInteger); overload;
function NStr(const A: IInteger; Base: TBase = 0): RetString; overload;
function NStr(const A: IInteger; const Format: TStrFormat): RetString; overload;
NStr als MyRec.ToString
Und die anderen Methoden haben fast alle den wichtigsten Parameter als Ersten,
somit kann ich es als Methode in einen Record verschieben,
und die Methode einfach als "external" deklarieren, womit dieser Parameter zum Self wird und direkt auf die
DLL verweist.
Standard-Oprtationen ala ADD, SUB und MUL, lassen sich dann zusätzlich auch schön in ClassOperator kapseln.
Die Grundfunktionen ließen sich dann also wie beim normalen Integer benutzen.
---
Es gab Anfangs sehr viele Fehler im Compilier,
siehe
Antwort #16, aber das ist nun behoben.
Es handelte sich um folgene 3 Arten von Fehlern:
overloaded functions with typeless parameters
NInts.NSet(var A: IInteger; const B; Size: Integer; Bits: Integer) name 'NInts_NSet_IIBInIn',
[Fehler] DECMath.dpr(66): Es existiert keine überladene Version von 'NSet' mit dieser Parameterliste.
unnecessary overload and parameters not declared
NECBuild name 'NECBuild_XXX',
[Fehler] DECMath.dpr(625): Es existiert keine überladene Version von 'NECBuild' mit dieser Parameterliste.
not overload, but still parameters declared
NInts.NCat(var A: IInteger; const B: IIntegerArray; Bits: Integer) name 'NInts_NCat_IIIAIn',
[Fehler] DECMath.dpr(109): ',' oder ';' erwartet, aber '(' gefunden
[Fehler] DECMath.dpr(109): '=' erwartet, aber ';' gefunden
[Fehler] DECMath.dpr(109): '=' erwartet, aber ')' gefunden
[Fehler] DECMath.dpr(109): Undefinierter Bezeichner: 'name'
[Fehler] DECMath.dpr(109): Inkompatible Typen: 'Integer' und 'String'
[Fehler] DECMath.dpr(110): '=' erwartet, aber '.' gefunden
...
Erstmal habe ich ein SharedMemory eingerichtet,
womit es keine Probleme mit dynamischen Arrays und keine grundsätzlichen Probleme mit den DelphiStrings mehr geben sollte.
Es gibt nun also die nur noch zwei Wrapper-Methoden, für die untypisierten Parameter.
Ja, da könnte ich mit einem JMP in Assembler den einen Rücksprung des Wrappers einsparen,
allerdings wäre dort der Aufwand höher im Assembler die richtige überladene Methode zu treffen.
Dann gibt es eine Methode, wo ich aus einem Objekt ein Interface mache,
durch eine zurätzliche Ableitung und cast des Result als Interface,
wo sich nicht viel optimieren lässt. bzw. es ist bestimmt den Aufwand nicht wert.
function Primes: TSmallPrimeSieve;
wurde zu
Delphi-Quellcode:
type
ISmallPrimeSieve = interface
['{897D56D7-7514-4473-917E-3DEFCD9A54E3}']
function MinPrime: Cardinal; // min. Prime, allways 2
function MaxPrime: Cardinal; // max. possible Prime, dependend from Compiler Version
function MinIndex: Cardinal; // min. Index, allways 1
function MaxIndex: Cardinal; // max. Index, see MaxPrime
function Count(LowerBound, UpperBound: Cardinal): Cardinal; // compute Primecount beetwen Bounds
function IndexOf(Value: Cardinal; LowerBound: Boolean{$IFDEF VER_D4H} = False{$ENDIF}): Cardinal; // give Primeindex of Value
procedure LoadCache(const FileName: AnsiString); // load a Prime Cache
procedure BuildCache(const FileName: AnsiString; Bound: Cardinal); // create and save a Cache
function GetPrime(Index: Cardinal): Cardinal;
property Prime[Index: Cardinal]: Cardinal read GetPrime; default; // return Prime with Index
function CacheMaxPrime: Cardinal; // max. cached Prime
function CacheMaxIndex: Cardinal; // max. cached Index of max. Prime
// cached min. Values are allways equal to MinPrime, MinIndex
end;
TSmallPrimeSieveIntf = class(TSmallPrimeSieve, ISmallPrimeSieve)
function MinPrime: Cardinal;
function MaxPrime: Cardinal;
function MinIndex: Cardinal;
function MaxIndex: Cardinal;
function GetPrime(Index: Cardinal): Cardinal;
function CacheMaxPrime: Cardinal;
function CacheMaxIndex: Cardinal;
end;
function PrimesIntf: ISmallPrimeSieve;
begin
Result := Primes as ISmallPrimeSieve;
end;
exports
PrimesIntf name 'Prime_Primes',
IsPrime name 'Prime_IsPrime';
(natürlich fehlt noch der Hook, um die Klassen auszutauschen, damit beim Create in der
DCU der richtige Typ erzeugt wird)
Da sich seit Delphi 2009 die AnsiString intern verändert haben, muß ich dort unbedingt noch etwas anpassen.
Jenes bezieht sich aber nur auf die wenigen function NStr(x: Interface): AnsiString;
.
Delphi 7:
Delphi-Quellcode:
type
PStrRec = ^StrRec;
StrRec = packed record
refCnt: Longint;
length: Longint;
end;
Delphi 11.3
Delphi-Quellcode:
type
// For System.pas internal use only.
// Note, this type is duplicated in getmem.inc for diagnostic purposes. Keep in sync.
PStrRec = ^StrRec;
StrRec = packed record
{$IF defined(CPU64BITS)}
_Padding: Integer; // Make 16 byte align for payload..
{$ENDIF}
codePage: Word;
elemSize: Word;
refCnt: Integer;
length: Integer;
end;
Const-Parameter in die
DLL rein, stellen nahezu keine Probleme dar,
da die zusätzlichen Felder für das alte Delphi praktisch unsichtbar/irrelevant sind.
Aber als Funktion-Result sieht das anders aus.
Da hätte ich die Wahl es via Wrapper auf WideString zu casten.
Allerdings werde ich hier das
SetString des neuen Delphi in die
DLL durchreichen
und dort aus dem Delphi7-AnsiString in NeuesDelphi-AnsiString erzeugen.
Denn das neue Delphi würde knallen, wenn es auf die nicht-vorhandenen Felder eines "alten" String zugreifen will -> BufferOverflow.
Außerdem stimmt die Brechnung StringPointer->MemoryBlock nicht überein und es würde spätestens neuen Delphi beim Freigeben des String in LStrClr->FreeMem knallen.
Es gibt aber noch ein paar weitere Hindernisse, mit anderen zu teilenden Dingen.
Exception-Klassen und andere übergebene Klassen und Objekte, wie z.B. TStream.
Siehe meine "TODO" bzw. Shared-Liste in
\Part_II\DECMath.Shared.txt.