Fortran 77 dll importieren + function aufrufen

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Hallo alle zusammen :D

Habe da ein Problem, was wie ich finde auf den meisten Seiten nicht wirklich gelöst wurde, daher dachte ich mir ich frage hier mal.
Ich habe ein .dll welche unter Fortran 77 compiliert wurde. Ich möchte diese in Python importieren, und eine Funktion in dieser .dll aufrufen. Die .dll hat den Namen Eth.dll und ich möchte gerne die Funktion CP_ABC_Eth(A,B,C) aus dieser aufrufen.

Hier vielleicht mal kurz was ich bisher habe:

from ctypes import *

# give location of dll
Eth = cdll.LoadLibrary("Eth.dll")

# call the functions in the dll
A=1
B=1
C=-1000
CP= Eth.CP_ABC_Eth(A,B,C)

print "CP"


wenn ich das versuche zu rechnen, erhalte ich folgende Fehlermeldung:

Traceback (most recent call last):
File "C:\...\testfile", line 11, in <module>
CP= Eth.CP_ABC_Eth(A,B,C)
WindowsError: exception: access violation reading 0x00000001

Hoffe mir kann einer weiterhelfen.

Vielen Dank im Voraus :mrgreen:
lunar

@liddokun88: Versuche einmal, die Signatur dieser Funktion so wie in der ctypes-Dokumentation angegeben zu spezifizieren. Ohne wird implizit angenommen, dass die Argumente von C-Typ "int" sind.
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Viellen Dank erstmal für die Anwort.

Irgendwie stehe ich da auf dem Schlauch. Die dll an sich wird bei mir eingelesen, aber der Aufruf der Funktion klappt ja nicht. Aber in der Docu finde ich unter ctypes nicht unbedingt wie du das meinst mit dem deklarieren als andere Datentyp. Für die Inputvariablen A,B,C ist das kein Problem, aber für die Funktion finde ich nichts direkt.
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Hallo alle zusammen.

Bin der Sache näher gekommen. Ich kann nun die dll einlesen und die Funktion darin aufrufen.
Es rechnet zwar noch nicht richtig, aber denke das bekommt man auch noch hin.
Vom Syntax her sieht das nun wie folgt aus:

Code: Alles auswählen

from ctypes import *

# give location of dll
Eth = windll.LoadLibrary("Eth.dll");

# call the functions in the dll
def CP_ABC(A,B,C):
    A =c_double()
    B=c_double()
    C =c_double()
    CP_ABC= Eth.CP_AB_Eth(byref(A),byref(B),byref(C))
Wenn ich nun die definierte Funktion CP_ABC aufrufe,die entsprechenden inputparameter eingebe, dann rechnet er mir aber noch unsinn aus. wenn ich also z.B.

Code: Alles auswählen

print CP_ABC(10,100,-1000)
eingebe, erhalte ich als Lösung None . Es sollte aber eigentlich ein double heraus kommen. Hat da einer eine Ahnung woran das liegen kann?
Zuletzt geändert von Anonymous am Mittwoch 7. März 2012, 11:48, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
deets

1. Bitte benutz Python code-tags, damit man das lesen kann
2. Du hast das immer noch nicht verstanden, wie du einer Funktion eine Signatur verpasst. Damit kann das ganze dann nicht funktionieren. Bitte erzaehl uns doch mal, wie genau die Funktion deklariert ist. Also in C zb sowas wie

Code: Alles auswählen

int do_somenthing(int a, int *b);
Anhand dieser Signatur musst du dann ctypes mitteilen, wie die Parameter der Funktion auszusehen haben.
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Die Funktion ist wie gesagt Fortran 77 und vom Typ

Code: Alles auswählen


REAL*8 FUNCTION CP_ABC_ETH (REAL*8 A,REAL*8 B, REAL*8 C)

es handelt sich also hierbei beim In- und Output um double Werte.
BlackJack

@liddokun88: Deine `CP_ABC()`-Funktion gibt `None` zurück weil das alle Python-Funktionen machen, bei denen man nicht explizit etwas zurück gibt. Dafür gibt es die ``return``-Anweisung. Stell Dir einfach ein ``return None`` am Ende von jeder Funktionsdefinition vor. An der Stelle solltest Du vielleicht ein Python-Tutorial durcharbeiten für die Grundlagen.

Laut Fortran-Signatur erwartet die Funktion Argumente vom C-Typ `double`, Du übergibst in Deiner Funktion aber Werte vom C-Typ *Pointer auf `double`*.

Damit `ctypes` weiss wie die Argumente übergeben werden müssen und welchen Typ das Ergebnis hat, musst Du wie schon gesagt diese Informationen an die dafür vorgesehenen Attribute des Funktionsobjektes binden, das Du von der DLL abfragst. Siehe den Abschnitt in der Dokumentation, den ich weiter oben verlinkt habe und auch den darauf folgenden Abschnitt für den Typ des Rückgabewertes.
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Ich habe mal versucht aus ctypes schlau zu werden. Wahrscheinlich ist das aber auch wieder total falsch.
Wäre nett wenn einer mal sagen könnte wie das den nun in meinem bsp. aussehen muss.

Code: Alles auswählen


# call the functions in the dll
def CP_ABC(A,B,C):
        CP = Eth.CP_ABC_Eth  #name of function
        CP.restype = c_double   #deklaration output
        CP.argtypes= [c_double,c_double,c_double]   #deklaration input
        CP = CP(p,t,x)
        return CP

bei der Variante erhalte ich ebenfalls einen Fehler und zwar:

Code: Alles auswählen

  File "C:\...\testfile", line 12, in CP_ABC
    CP = CP(A,B,C)
WindowsError: exception: access violation reading 0x00000000
BlackJack

@liddokun88: Das sollte eigentlich richtig sein. Jetzt könnte es noch sein, dass die DLL eine andere Aufrufkonvention verwendet. Also das man sie mit `windll` statt `cdll` laden muss.

Oder die Parameterübergabe funktioniert bei Fortran ganz anders als bei C. Dann wäre ich ein wenig ratlos.

Bei Deinem Beispiel ist die Python-Funktion relativ nutzlos, weil man das erstellen des C-Types-Funktionsobjektes auch aus der Funktion heraus ziehen kann — das muss man ja nicht jedes mal aufs neue bei jedem Aufruf machen. Dann besteht die Python-Funktion nur noch aus einem weiterreichen der Argumente.

Etwas ungünstig ist es auch den selben Namen (`CP`) innerhalb einer Funktion für so grundverschiedene Dinge zu verwenden.
BlackJack

@liddokun88: Kurze Suche im Netz nach der Aufrufkonvention von Fortran (Suchanfrage „fortran77 library calling convention from c”) ergab als ersten Treffer folgende Seite: http://www.math.utah.edu/software/c-wit ... ence-diffs

Demnach muss man immer Pointer übergeben, auch auf skalare Werte. Ungetestet:

Code: Alles auswählen

c_double_p = POINTER(c_double)

eth_lib = windll.LoadLibrary('Eth.dll') # oder `cdll`...

_cp_ab = eth_lib.CP_AB_Eth
_cp_ab.argtypes = [c_double_p, c_double_p, c_double_p]
_cp_ab.restype = c_double

def cp_abc(a, b, c):
    a = c_double(a)
    b = c_double(b)
    c = c_double(c)
    return _cp_ab(byref(a), byref(b), byref(c))
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Danke für die schnelle Nachricht. Hab das mal geändert so dass es nun wie folgt aussieht.

Code: Alles auswählen


# import all functions from ctypes
from ctypes import *

# deklaration of double in/output
c_double_p = POINTER(c_double)    

# give location of dll
Eth = windll.LoadLibrary("Eth.dll");

# call the functions in the dll
def CP_ABC(A,B,C):
    _cp_ptx = Eth.CP_ABC_Eth     #select function in dll
    _cp_ptx.argtypes = [c_double_p,c_double_p,c_double_p] # input to double
    _cp_ptx.restype = c_double_p       
#   change imput implicit to double
    A = c_double(A)
    B = c_double(B)
    B = c_double(C)
#   return value to console
    return _cp_ptx(byref(A), byref(B), byref(C))
Als Rückgabewert erhalte ich

Code: Alles auswählen

_main__.LP_c_double object at 0x02983260>
Was sagt mir das jetzt ?
Zuletzt geändert von liddokun88 am Donnerstag 8. März 2012, 14:24, insgesamt 1-mal geändert.
BlackJack

@liddokun88: Das sagt Dir dass `ctypes` versucht den Rückgabewert als „Pointer auf double” zu interpretieren, weil Du das fälschlicherweise so angegegen hast. `restype` muss nur `c_double` sein.

Und ich hätte ja einen `NameError` erwartet, weil den Namen `p`, `t`, und `x` nirgends etwas zugewiesen wird.
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

ahhh endlich funktioniert es :D :D :D :D

Hattest recht mit dem c_double bei restype. Habe das geändert und alles geht.

Viellen Dank für die Hilfe :D
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

als nachtrag nochmal eine Frage,

wie kann ich dem Algorithmus sagen, das der input C z.B. ein Array sein muss ?

Also angenommen C ist ein double Array mit 8 Argumenten, wie müsste ich das Argument C dann ändern?
BlackJack

@liddokun88: Arraytypen werden bei `ctypes` mit dem ``*``-Operator erzeugt.

Code: Alles auswählen

In [1]: import ctypes as ct

In [2]: ct.c_double * 8
Out[2]: <class '__main__.c_double_Array_8'>
liddokun88
User
Beiträge: 18
Registriert: Montag 5. März 2012, 16:11

Ich stehe heute auf dem Schlauch, wie soll das nun aussehen?

Code: Alles auswählen

from ctypes import *
c_double_p = POINTER(c_double)    
test = windll.LoadLibrary("test.dll");

# call the functions in the dll
def A(B,C):        # wobei C ein double array mit 8 argumenten ist
     A = test.A_BC     #select function in dll
     A.argtypes = [c_double_p,ctypes.c_double * 8] # input to double
     A.restype = c_double_p       

     B = c_double(B)

#   return value to console
    return A(byref(B), byref(class '__main__.c_double_Array_8'))
 
meinst du das so in der Richtung?
deets

Das ist doch noch nicht mal gueltiges Python.

Es steht in der ctypes-Doku wie man Array-Typen anlegt, und damit arbeitet um konkrete Arrays und Referenzen darauf zu erzeugen.

http://python.net/crew/theller/ctypes/t ... tml#arrays
Antworten