Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - probleme mit c++ dll, delphi typbezeichner in struct


Atoll - Di 02.10.07 17:56
Titel: probleme mit c++ dll, delphi typbezeichner in struct
Hallo,

ich habe folgendes problem: ich moechte eine funktion aus einer c++ library verwenden. Die funktion hat ein struct (record in c++) als Parameter. Das Problem besteht darin das das struct ein Feld namens 'function' enthaelt und dieser Bezeichner von delphi bereits verwendet wird. Gibt es eine Moeglichkeit die dll funktion trotzdem zu verwenden?



Atoll


Lossy eX - Di 02.10.07 18:07

Das sind doch nur Bezeichner innerhalb deiner Programmsrache. Damit Delphi weiß auf welches Feld du zugreifen möchtest. Im echten Programm befinden sich keine Bezeichner mehr dort wird nur noch direkt auf den Speicherbereich zugeriffen.

Mit anderen Worten du kannst es "knoedeltroeten" nenne. Das juckt niemanden außer den der deinen Code lesen muss. ;)


Atoll - Mi 03.10.07 03:56

okay danke fuer die antwort.

es will allerdings immer noch nicht funktionieren. ich nehme anscheinend falsche typen.

Das hier ist der c++ typ einer funktion


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
Data Type: gsl_function
    This data type defines a general function with parameters.

    double (* function) (double x, void * params)
        this function should return the value f(x,paramsfor argument x and parameters params 
    void * params
        a pointer to the parameters of the function


diese funktion wird an eine andere funktion uebergeben:


C#-Quelltext
1:
2:
3:
int gsl_integration_qags (const gsl_function * f, double a, double b,
double epsabs, double epsrel, size t limit, gsl_integration workspace *
workspace, double * result, double * abserr)


bis auf die gsl_function habe ich alle typen ordentlich definiert in delphi. aber bei der gsl_function hapert es. wie wuerdet ihr die definieren?

Hier noch ein c++ beispielprogramm zu gsl_integration_qags:



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
double f (double x, void * params) {
double alpha = *(double *) params;
double f = log(alpha*x) / sqrt(x);
return f;
}
int
main (void)
{
gsl_integration_workspace * w
= gsl_integration_workspace_alloc (1000);
double result, error;
double expected = -4.0;
double alpha = 1.0;
gsl_function F;
F.function = &f;
F.params = α
gsl_integration_qags (&F, 0101e-71000,w, &result, &error);
printf ("result = % .18f\n", result);
printf ("exact result = % .18f\n", expected);
printf ("estimated error = % .18f\n", error);
printf ("actual error = % .18f\n", result - expected);
printf ("intervals = %d\n", w->size);
return 0;
}



die DLL ist uebrigends die Gnu Scientific Library (libgsl)

Danke im vorraus

Atoll

Moderiert von user profile iconNarses: Code- durch C#-Tags ersetzt


Stefan.Buchholtz - Mi 03.10.07 11:07

Ok, gsl_function definiert also einen Zeiger auf eine Funktion, die einen double und einen untypisierten Pointer als Argumente bekommt und ein double zurückgibt.

Das sieht in Delphi so aus:


Delphi-Quelltext
1:
2:
type
  TGslFunction = function(x: Double; params: Pointer): Double; stdcall;



Die Funktion gs_integration_qags müsse dann so definiert werden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function gsl_integration_qags(f: TGslFunction; 
                              a, b, epsabs, epsrel: Double; 
                              limit: Cardinal;
                              workspace: PGslIntegration;
                              var result: Double;
                              var abserr: Double): Integer; stdcallexternal 'libgsl.dll';


Bei limit bin ich mir aber nicht ganz sicher - ist size_t in C ein 32 Bit oder 64 Bit Typ?

Stefan


BenBE - Mi 03.10.07 20:01

u.U auch beachten, welches Speicheralignment benutzt wird.

Bei Dlphi kann man für einzelne Record-Typen das Alignment mit Packed Record abschalten.


Atoll - Mi 03.10.07 20:30

Sollte GSL_function nicht eher von folgender struktur sein? :


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
type 
   evalfunc = function(x:double; param:pointer):double;
   gslfunc = record
       func : ^evalfunc;
       params : pointer;
   end;


zumindest ist Gsl_function in dem c++ beispielprogram ein record typ

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


BenBE - Mi 03.10.07 21:56

Funktionspointer OHNE ^ vor'm Typbezeichner.


Atoll - Do 04.10.07 03:58

Danke fuer die Hilfe, jetzt klappt es.

Falls jemand interesse hat die dll zu benutzen hier ein wenig code


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
type

    TGSLfunction = record
      func : function(x:double; params:pointer):double; CDECL;
      params : pointer;
    end;

    TWorkspace = record
    limit : LongWord;
    size : LongWord;
    nrmax : LongWord;
    i : LongWord;
    maximum_level : LongWord;
    alist : Array of double;
    blist : Array of double;
    rlist : Array of double;
    elist : Array of double;
    order : Array of LongWord;
    level : Array of LongWord;
    end;

var

function mathfunction(x:double; params:pointer):double;CDECL;

//differentiation function

function gsl_diff_central (f:pointer;
                           x:double;
                           result:pointer;
                           abserr:pointer):integer;stdcallexternal 'libgsl';

//integration function

function gsl_integration_workspace_alloc (n:LongWord):pointer; CDECLexternal 'libgsl';

function gsl_integration_qags(f: pointer;
                              a, b, epsabs, epsrel: Double;
                              limit: Cardinal;
                              workspace: pointer;
                              var result: Double;
                              var abserr: Double): Integer; stdcallexternal 'libgsl.dll';

procedure gsl_integration_workspace_free(w:pointer); CDECLexternal 'libgsl';


...



function mathfunction(x:double; params:pointer):double; CDECL;
var parameter:Array of double;
begin
Setlength(parameter,1);
parameter := params;
mathfunction := ln(parameter[0]*x) / sqrt(x);
end;

procedure TSDIAppForm.Button2Click(Sender: TObject);
var w:^TWorkspace;
    result,error : double;
    functest : TGSLfunction;
    parameters : Array of double;

begin
  //define test-function
  Setlength(parameters,1);
  parameters[0]:=2;
  functest.func := mathfunction;
  functest.params := parameters;

  gsl_diff_central (@functest, 4.0, @result, @error);

  w:=gsl_integration_workspace_alloc(1000);
  gsl_integration_qags(@functest, 0100.00000011000, w, result, error);
  gsl_integration_workspace_free(w);

  label1.Caption:=floattostr(result);
end;



benutzt die funktionen zum integrieren und differenzieren aus der library


ich denke es ging vorher nicht da ich der mathfunction nicht CDECL nachgestellt hatte.

Atoll

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


Lossy eX - Do 04.10.07 09:23

cdecl vs stdcall: Die Übergabe der Parameter bei stdcall und cdecl sind eigentlich gleich. Bei cdecl werden sie nur von dem wieder entsorgt der sie an die Funktion übergeben hat. Wenn du also eine cdelc funktion hast und diese als stdcall aufrufst bleiben unweigerliche Parammeter auf dem Stack liegen. Wenn diese funktionen sehr häufig aufgerufen werden läuft dir irgendwann der Stack voll. Überprüfe bitte GENAU welche Aufrufkonvention benutzt wird. Und setze diese dann einheitlich für alle deine Funktionen ein. Ich habe noch nie eine DLL gesehen bei der unetrschiedliche Konventionen gleichzeitig benutzt wurden.

TWorkspace: Bei den offenen Arrays handelt es sich normal nur um Pointer. Und wenn ich mich nicht vertue dann packt Delphi vor dem ersten Element noch die Größe mit in den Speicher. Wenn du also versuchst die Größe der Arrays abzufragen sollte es unweigerlich knallen, da C++ das höchstwahrscheinlich anders löst. (Das weiß ich nicht genau) Aber mit offen arrays wäre ich vorsichtig.

mathfunction: Da machst du etwas komisches. Du setzt die Länge eines arrays auf 1 und überschreibst es anschließend. Mich wundert es gerade, dass delphi das so ohne weiters schluckt. Aber für deinen Fall wäre wohl eher etwas wie folgendes das Bessere.

Delphi-Quelltext
1:
2:
3:
4:
function mathfunction(x:double; params:pointer):double; CDECL;
begin
  mathfunction := ln(pDouble(parameter)^ * x) / sqrt(x);
end;

Bzw dann genügt es auch wenn du den Pointer auf ein einfaches Double übergibst. Und wenn du ein Array brauchst dann solltest du den Parameter als pDoubleArray casten und das benutzen.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
type
  pDoubleArray = array of Double;

//  pDoubleArray = array [0..$FFFF] of Double;
  // oder so, weil es sich sonst wieder um offene Arrays handelt. Siehe oben.
  // Nur dann kannst du die Größe nicht sinnvoll abfragen.

  pDoubleArray(parameter)^[0]


Atoll - Fr 05.10.07 00:56

bloede frage,

aber wo kann ich die aufrufkonvention CDECL oder stdcall anhand der library erkennen?


Lossy eX - Fr 05.10.07 09:17

Diese Frage ich nicht blöd sondern bei C++ durchaus berechtigt. In Pascal steht die Konvention immer hinter der funktion. In C++ eher immer davor. Aber es wird eigentlich grundsätzlich immer per Präprozessor gelöst.

Als kleines Beispiel mal ein Ausschnitt aus einem Header von mir.

Quelltext
1:
2:
3:
4:
5:
6:
7:
#define TSAPI __declspec(dllimport)

// diverse typdefinitionen

// viele viele Konstanten....

TSAPI tsBool tsInit (tsEnum Names);

Wie du siehst steht bei der Methode noch das Define TSAPI davor. Der Präprozessor von C/C++ ersetzt das Define durch dessen Inhalt. __declspec(dllimport) bedeutet cdecl. Es gibt aber auch so etwas wie __cdecl das muss dann aber zwischen Rückgabewert und Methodennamen stehen. Bedeutet aber eigentlich das gleiche. Ansonsten gibt es noch so etwas wie __stdcall. Bzw gibt es noch das Standarddefine WINAPI was auch nur wieder auf __stdcall zeigt. Und wenn gar nichts angegeben ist dann sollte es auch stdcall sein. Aber das kann man sicherlich auch noch global in den Projekteinstellungen beinflussen. Also es gibt leider viel zu viele Möglichkeiten das einzustellen.

Aber es ist normal immer einheitlich in einer Library. Und spezielle Windows DLL sind eher stdcall wohingegen open source Bibliotheken eher cdecl benutzen. Da Linux normal mit cdecl arbeitet und man die dann der einfach heit auch so lässt.

Im Zweifel benutzt mal cdecl als Konvention. Da dadurch der Aufrufende die Paramater wegräumt werden im Falle einer stdcall methode die Parameter 2 mal weggeräumt und dabei zerschießt du dir recht schnell den Stack. Wenn also Fehler auftreten ist es vermutlich eine stdcall Methode. Wenn nicht und die methode auch arbeitet scheint es sich um cdecl zu handeln. Aber es ist logischerweise besser es vorher zu wissen. ;)