Fortran: Fortran und C: Fortran 2003
<<< zur Fortran-Startseite | |
<< Fortran und C | F >> |
< Fortran 90/95 und C | Fortran 2003 und C > |
In Fortran 2003 ist es viel einfacher auf C zu zugreifen, als in Fortran 95. Es wurde im Fortran 2003-Standard ein intrinsisches Modul namens iso_c_binding
vorgesehen, das die zum Zugriff auf C-Programme nötigen Elemente enthält.
Ein einfaches Beispiel
[Bearbeiten]Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003-Code: bsp.f95
Fortran 2003 (oder neuer)-Code |
program bsp implicit none interface function addition( a, b ) bind( c[, name="c_func"] ) use, intrinsic :: iso_c_binding real( kind = c_float ), value :: a real( kind = c_float ), value :: b real( kind = c_float ) :: addition end function addition end interface write (*,*) addition( 2.5, 3.3 ) ! Ausgabe: 5.8 end program bsp |
C-Code: bsp.c
Programmcode |
float addition(float a, float b) { return (a + b); } |
Makefile:
Programmcode |
FC = g95 # oder gfortran, ... CC = gcc # oder icc, ... bsp: bsp_c.o bsp_f95.o $(FC) -o bsp bsp_c.o bsp_f95.o bsp_c.o: bsp.c $(CC) -c -o bsp_c.o bsp.c bsp_f95.o: bsp.f95 $(FC) -c -o bsp_f95.o bsp.f95 .PHONY: clean clean: rm *.o |
Was ist neu gegenüber Fortran 95?
|
... | bind-Attribut, stellt unter anderem die Interoperabilität mit C bezüglich Prozedurnamenskonventionen nach der Übersetzung sicher. Mittels des optionalen Arguments "name" kann die Funktion in Fortran umbenannt werden. "c_func" ist dabei der Name der Funktion in C. |
|
... | Einbindung des intrinsischen Moduls iso_c_binding
|
|
... | C-Datentyp float .
|
|
... | call by value |
Datentyp-Zuordnung
[Bearbeiten]Das iso_c_binding
-Modul stellt benannte Konstanten zur Verfügung, die bei Fortran-Datentypen als kind-Wert zu verwenden sind, um den jeweiligen C-Datentyp zu charakterisieren. Weist eine solche Konstante einen negativen Wert auf, dann ist keine Entsprechung von Fortran-Datentyp zu C-Datentyp vorhanden.
Fortran-Datentyp | Benannte iso_c_binding -Konstante (kind-Wert)
|
C-Datentyp |
---|---|---|
integer | c_int | int |
c_short | short int | |
c_long | long int | |
c_long_long | long long int | |
c_signed_char | signed char, unsigned char | |
c_size_t | size_t | |
c_int8_t | int8_t | |
c_int16_t | int16_t | |
c_int32_t | int32_t | |
c_int64_t | int64_t | |
c_int_least8_t | int_least8_t | |
c_int_least16_t | int_least16_t | |
c_int_least32_t | int_least32_t | |
c_int_least64_t | int_least64_t | |
c_int_fast8_t | int_fast8_t | |
c_int_fast16_t | int_fast16_t | |
c_int_fast32_t | int_fast32_t | |
c_int_fast64_t | int_fast64_t | |
c_intmax_t | intmax_t | |
c_intptr_t | intptr_t | |
real | c_float | float |
c_double | double | |
c_long_double | long double | |
complex | c_float_complex | float _Complex |
c_double_complex | double _Complex | |
c_long_double_complex | long double _Complex | |
logical | c_bool | _Bool |
character | c_char | char |
Quelle: J3/04-007 Fortran 2003 Working Draft |
Das iso_c_binding
-Modul stellt keine speziellen kind-Werte für unsigned
-Integer-Datentypen zur Verfügung. Im Bedarfsfall sind die entsprechenden kind-Werte für die signed
-Datentypen zu verwendet.
Nicht jeder Fortran-Compiler unterstützt alle genannten C-Datentypen und die unterstützten Datentypen können sich compilerspezifisch in der Byteanzahl unterscheiden. Die nächste Tabelle zeigt kurz auf, welche kind-Konstanten derzeit (1. Dez. 2007) von einigen Compilern definiert werden.
c_int | c_short | c_long | c_long_long | c_signed_char | c_size_t | c_int8_t | |
---|---|---|---|---|---|---|---|
g95 | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
gfortran | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
ifort | 4 | 2 | 4 | 8 | 1 | 4 | 1 |
f95 | 4 | 2 | 4 | 8 | 1 | 4 | |
c_int16_t | c_int32_t | c_int64_t | c_int_least8_t | c_int_least16_t | c_int_least32_t | c_int_least64_t | |
g95 | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
gfortran | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
ifort | 2 | 4 | 8 | 1 | 2 | 4 | 8 |
f95 | 1 | 2 | 4 | 8 | |||
c_int_fast8_t | c_int_fast16_t | c_int_fast32_t | c_int_fast64_t | c_intmax_t | c_intptr_t | c_float | |
g95 | 1 | 4 | 4 | 8 | 4 | 4 | 4 |
gfortran | (1) | (1) | (1) | (1) | 8 | (1); 4 oder 8 | 4 |
ifort | 1 | 4 | 4 | 8 | 8 | 4 | 4 |
f95 | 1 | 2 | 4 | 8 | 8 | 4 | |
c_double | c_long_double | c_float_complex | c_double_complex | c_long_double_complex | c_bool | c_char | |
g95 | 8 | -1 | 4 | 8 | -1 | -1 | 1 |
gfortran | 8 | (1), oft 10 | 4 | 8 | (1), oft 10 | 1 | 1 |
ifort | 8 | -1 | 4 | 8 | -1 | 1 | 1 |
f95 | 8 | -3 | 4 | 8 | -3 | 1 | 1 |
(1) Der Wert ist systemabhängig
- g95: g95 0.91! Nov 29 2007
- gfortran: GNU Fortran (GCC) 4.3.0 20071201 (experimental)
- ifort: Intel Fortran Compiler 10.1 20070913
- f95: Sun Studio Express, June 2007
positive Zahl | Konstante ist bekannt, C-Datentyp wird unterstützt |
negative Zahl | Konstante ist bekannt, C-Datentyp wird nicht unterstützt |
unbekannte Konstante |
„call by value“ vs. „call by reference“
[Bearbeiten]Im einführenden Beispiel wurden die Funktionsargumente „call by value“ übergeben. Das Variablenattribut value
stellt dieses Verhalten sicher. Wird dieses Attribut nicht gesetzt, so gilt „call by reference“.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none interface subroutine zahl( a, b ) bind( c ) use, intrinsic :: iso_c_binding integer( kind=c_int ), value :: a integer( kind=c_int ) :: b end subroutine zahl end interface call zahl(5, 7) ! Ausgabe: ! Ergebnis = 35 end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void zahl(int a, int *b) { printf("%s%d\n", "Ergebnis = ", a * *b); } |
Globale C-Variablen
[Bearbeiten]Muss in Fortran auf globale C-Variablen zugegriffen werden, so sind diese im Gültigkeitsbereich eines Fortran-Modul zu spezifizieren.
Felder
[Bearbeiten]Interoperabilität zwischen Fortran und C ist nur mit Feldern definierter Größe gegeben. Allozierbare Felder oder Zeigerfelder sind nicht erlaubt.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none integer, dimension( 3 ) :: a = (/ 1, 2, 3 /) interface subroutine feld1( f ) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), dimension(*) :: f end subroutine feld1 end interface write (*,*) "Feld a vorher: ", a call feld1( a ) write (*,*) "Feld a nachher: ", a ! Ausgabe: ! Feld a vorher: 1 2 3 ! Feld a nachher: 999 2 3 end program bsp |
Programmcode |
void feld1(int f[]) { f[0] = 999; } |
Übergabe von Zeichenketten
[Bearbeiten]Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): nein
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- gfortran lehnt dies ab, da dies ungültiger Fortran-Syntax ist: Fehler: Character argument 'str_in' at (1) must be length 1 because procedure 'string1' is BIND(C)
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char, len=* ) :: str_in ! Ungültiges Fortran 2003 da nur len=1 erlaubt ist end subroutine string1 end interface call string1( c_char_"Greetings from Fortran" // c_null_char ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void string1(char str_in[]) { printf("%s \n", str_in); } |
Für len=1 funktioniert das Beispiel mit allen angeführten Compilern. Das len
-Attribut kann im Übrigen auch weggelassen werden; einige unterstützen als compilerspezifische Erweitungen auch andere Längen. Im Fortran 2003-Working-Draft wird in „Note 15.23“ eine andere Möglichkeit für die Übergabe von Zeichenketten angeführt. Diese folgt im wesentlichen der Annahme:
- C-Zeichenketten sind
char
-Felder mit einem terminierenden\0
-Zeichen und können deshalb in Fortran als Felder vom Datentypcharacter
mit einem kind-Wertc_char
angesprochen werden.
Daher wird im Interface der Dummy-Parameter in Form eines Feldes aus Zeichen des Typs c_char
deklariert. Das entspräche auch genau der aufzurufenden C-Funktion. Allerdings ist solcherart verfasster Code dann nicht mit allen Compilern übersetzbar (Stand Juli 2007).
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: nein
Anmerkungen:
- Sun Studio Express-Fehlermeldung:
- ... Zusicherung >>addr<< nicht erf?llt.
- "bsp.f95", Line = 13, Column = 1: INTERNAL: Interrupt: Abgebrochen
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char ), dimension(*) :: str_in end subroutine string1 end interface call string1( c_char_"Greetings from Fortran" // c_null_char ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
Beispiel:
Damit das obige Beispiel auch mit dem "Sun Studio-Fortrancompiler" funktioniert, darf der String nicht direkt dem Unterprogramm übergeben werden, sondern muss zuvor in einer Variablen abgelegt werden.
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp use, intrinsic :: iso_c_binding implicit none character(len=35) :: str interface subroutine string1( str_in ) bind( c ) use, intrinsic :: iso_c_binding character( kind=c_char ), dimension(*) :: str_in end subroutine string1 end interface str = "Greetings from Fortran" // c_null_char call string1( str ) ! Ausgabe: ! "Greetings from Fortran" end program bsp |
Benannte iso_c_binding
-Konstanten für Zeichen mit spezieller Bedeutung in C:
Benannte Konstante | Wert |
---|---|
c_null_char | '\0' |
c_alert | '\a' |
c_backspace | '\b' |
c_form_feed | '\f' |
c_new_line | '\n' |
c_carriage_return | '\r' |
c_horizontal_tab | '\t' |
c_vertical_tab | '\v' |
Enumerationen
[Bearbeiten]Mit Fortran 2003 sind auch in dieser Programmiersprache Enumerationen (Aufzählungstypen) möglich. Die Werte in einer solchen Enumeration besitzen einen integer
-Datentyp. Der kind
-Wert ist nicht festgelegt, wird jedoch so gewählt, dass im Rahmen der jeweiligen Möglichkeiten alle Enumeratoren erfasst sind.
Die von C-Enumerationen bekannten Eigenschaften gelten gleichermaßen für Fortran-Enumerationen, z.B.:
- ohne explizite Zuweisung von Werten wird mit dem Wert 0 gestartet.
- ohne explizite Zuweisung von Werten wird in der Anordnungsreihenfolge der Elemente sukzessiv immer um 1 hochgezählt.
- Wurde dem Vorgängerelement eine Ganzzahl zugewiesen, dem Element jedoch nicht, so ist der Wert dieses Elementes die dem Vorgängerelement zugeordnete Ganzzahl + 1.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: nein
- Sun Studio Express - June 2007: nein
Anmerkungen:
- Sun-Fortran-Compiler und Intel-Fortran-Compiler unterstützen momentan noch keine Enumerationen.
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
program bsp implicit none enum, bind(c) enumerator :: MO=1, DI=2, MI=3, DO=4, FR=5, SA=6, SO=7 end enum interface subroutine tag( w ) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), value :: w end subroutine tag end interface call tag(MI); ! Ausgabe: ! Mittwoch end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> typedef enum { MO=1, DI=2, MI=3, DO=4, FR=5, SA=6, SO=7 } wochentag; void tag(wochentag w) { switch(w) { case MO: printf("Montag\n"); break; case DI: printf("Dienstag\n"); break; case MI: printf("Mittwoch\n"); break; case DO: printf("Donnerstag\n"); break; case FR: printf("Freitag\n"); break; case SA: printf("Samstag\n"); break; case SO: printf("Sonntag\n"); break; default: printf("Kein Tag\n"); } } |
Zeiger
[Bearbeiten]Für das C-Zeiger-Handling stellt Fortran 2003 im iso_c_binding
-Modul den Datenverbund c_ptr
und einige Unterprogramme bereit.
UP | Beschreibung |
---|---|
l = c_associated ( c_ptr1 [, c_ptr2] ) | Prüft den Assoziationsstatus von c_ptr1. Diese Funktion ermittelt also, ob c_ptr1 überhaupt assoziert ist, oder ob c_ptr1 mit c_ptr2 assoziert ist. |
c_ptr = c_loc ( x ) | Gibt die Adresse von x zurück. |
c_f_pointer ( c_ptr, fptr [, shape] ) | Wandelt einen C-Zeiger c_ptr in einen Fortran-Zeiger fptr um. Die Vorgabe von shape ist nur dann erforderlich und möglich, wenn fptr ein Feld ist. fptr ist intent( inout ) .
|
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- Sun-Linker-Warnmeldung:
- Warning: alignment 4 of symbol `ptr1', ... in bsp_c.o is smaller than 16 in bsp_f90.o
- Intel-Linker-Warnmeldung:
- Warning: alignment 4 of symbol `ptr1', ... in bsp_c.o is smaller than 8 in bsp_f90.o
Fortran-Code bsp.f95:
Fortran 2003 (oder neuer)-Code |
module cglob use, intrinsic :: iso_c_binding type( c_ptr ), bind( c ) :: ptr1, ptr2, ptr3 real( kind=c_float ), target, bind( c ) :: a, b end module cglob program bsp use cglob implicit none real, pointer :: p => null() ! *** Zuordnungsstatus *** write (*,*) "Ist ptr1 assoziert? ", c_associated( ptr1 ) write (*,*) "Ist ptr2 assoziert? ", c_associated( ptr2 ) write (*,*) "Ist ptr2 mit ptr3 assoziert? ", c_associated( ptr2, ptr3 ) write (*,*) "Ist ptr2 mit &a assoziert? ", c_associated( ptr2, c_loc(a) ) ! *** Wert von ptr2? *** call c_f_pointer( ptr2, p ) write (*,*) "Wert von ptr2? ", p ! *** ptr1 neu setzen *** ptr1 = c_loc( b ) ! *** Wert von ptr1 *** call c_f_pointer( ptr1, p ) write (*,*) "Wert von ptr1? ", p ! Ausgabe: ! Ist ptr1 assoziert? F ! Ist ptr2 assoziert? T ! Ist ptr2 mit ptr3 assoziert? F ! Ist ptr2 mit &a assoziert? T ! Wert von ptr2 (bzw. p)? 5555.66 ! Wert von ptr1 (bzw. p)? -12.3 end program bsp |
C-Code bsp.c:
Programmcode |
float a = 5555.66; float b = -12.3; float *ptr1 = 0; float *ptr2 = &a; float *ptr3 = &b; |
Nun ist die Zeiger-Verwendung wie im obigen Beispiel skizziert, eher die Ausnahme als die Regel.
Wesentlich häufiger trifft man Zeiger in C-Bibliotheken im Zusammenhang mit Funktionen an. Dort dienen sie aus Anwendersicht als return
-Werte oder der Parameterübergabe „call-by-reference“. Oft sind dabei auch Zeiger auf Strukuren im Spiel. Näheres dazu folgt im nächsten Abschnitt.
Für C-Zeiger auf den Wert NULL
bietet das ISO-C-Binding-Modul die Konstante C_NULL_PTR
.
Datenverbund
[Bearbeiten]Die einzelnen Datenelemente der Struktur / des Datenverbundes müssen in Fortran und C selbstverständlich äquivalenten Datentyp aufweisen. Die Bezeichnungen der jeweiligen Datenelemente müssen nicht beibehalten werden. Sehr wohl müssen aber die Positionen der Einzelelemente im Fortran-Datenverbund der nachzubildenden C-Struktur entsprechen.
Für die Gewährleistung der Interoperabilität darf ein an C gebundener Datenverbund in Fortran (struct
) keine Zeiger mit dem pointer
-Attribut oder allozierbaren Felder als Datenelemente enthalten. Sind in der C-Struktur Zeiger vorhanden, so sind diese im entsprechenden Fortran-Datenverbund mittels type( c_ptr )
zu beschreiben.
Für Bitfelder oder Unions gibt es in Fortran keine entsprechenden Gegenstücke.
Beispiel:
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
- gfortran: Im Gegensatz zu den anderen Compilern müssen bei der Deklaration der Subroutine print_v im Interface die Klammern für die leere Parameterliste vor dem bind(C) zwingend angestschrieben werden, ansonsten Fehlermeldung beim Compilieren; diese Klammer ist im Fortran 2003 Standard vorgeschrieben.
Fortran 2003 (oder neuer)-Code |
module cglob use, intrinsic :: iso_c_binding type, bind( c ) :: verbund integer( kind=c_int ) :: a real( kind=c_float ) :: b end type verbund type(verbund), bind( c ) :: v end module cglob program bsp use cglob implicit none interface subroutine set_v( a_in, b_in) bind( c ) use, intrinsic :: iso_c_binding integer( c_int ), value :: a_in real( c_float ), value :: b_in end subroutine set_v subroutine print_v() bind( c ) end subroutine print_v end interface call set_v( 5, -10.9) call print_v() write (*,*) "Fortran-Ausgabe: ", v v%a = 99 call print_v() write (*,*) "Fortran-Ausgabe: ", v ! Ausgabe: ! C-Ausgabe: 5 -10.900000 ! Fortran-Ausgabe: 5 -10.9 ! C-Ausgabe: 99 -10.900000 ! Fortran-Ausgabe: 99 -10.9 end program bsp |
Programmcode |
#include <stdio.h> typedef struct { int a; float b; } verbund; verbund v; void set_v(int a_in, float b_in) { v.a = a_in; v.b = b_in; } void print_v() { printf("C-Ausgabe: %i %f\n", v.a, v.b); } |
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 1
Bekannt seien zwei Funktionsprototypen in der Programmiersprache C:
Xyz *get_xyz();
und
void set_xyz(Xyz *x);
Xyz sei ein C-struct
, dessen Inhalt hier nicht näher interessiert.
Die Schnittstelle, mit dem in Fortran die C-Anbindung der Funktionen realisiert wird, könnte so aussehen:
interface function get_xyz() bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ) :: get_xyz end function get_xyz subroutine set_xyz( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine set_xyz end interface
Und schon können diese Funktionen auch in Fortranroutinen direkt Anwendung finden, z.B:
type( c_ptr ) :: x56 x56 = get_xyz() call set_xyz( x56 )
Wie man anhand dieses Beispiels erkennt, ist die interne Struktur von Xyz
in diesem Fall vollkommen irrelevant. Es ist nicht nötig, in der API-Dokumentation oder in den C-Headerdateien nachzuforschen, wie der C-struct
konkret aufgebaut ist. Für den Fortran-Programmierer ist dies immer ein type( c_ptr )
(natürlich nur, solange Zeiger auf Strukturen gefordert sind). Wird ein Zeiger des Typs c_ptr
nur via Fortranprogramm zwischen verschiedenen C-Funktionen übergeben oder zurückgeliefert, so ist auch keine Umwandlung in einen Fortran-Zeiger erforderlich und wäre mangels genauer Kenntnis des Aufbaus von Xyz
hier auch nicht möglich. Erst dann, wenn von Fortran aus auf einzelne Elemente eines C-struct
zugegriffen werden soll oder mit Kopien der Struktur hantiert wird, muss diese Struktur in Fortran nachgebaut werden.
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 2
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
module test use, intrinsic :: iso_c_binding implicit none type, bind( c ) :: A ! Das funktioniert nicht mit allen Kompilern; versuchen Sie ansonsten: ! type A ! sequence integer( c_int ) :: xc, yc type( c_ptr ) :: str end type interface function get_a() bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ) :: get_a end function get_a subroutine print_a( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine print_a end interface end module test program main use test implicit none type( c_ptr ) :: x type( A ), pointer :: fptr character( len=9 ), pointer :: strptr x = get_a() ! C-Ausgabe call print_a( x ) ! Ausgabe: ! x = 5 ! y = 997 ! str = Irgendwas ! Fortran-Ausgabe call c_f_pointer( x, fptr) call c_f_pointer( fptr%str, strptr ) ! <--- Fehlerquelle bei gfortran write(*,*) fptr%xc, fptr%yc, strptr ! Ausgabe ! 5 997 Irgendwas end program main |
Programmcode |
#include <stdlib.h> #include <stdio.h> typedef struct { int x; int y; const char *str; } A; A *get_a() { A *a = ( A * )malloc( sizeof( A ) ); a->x = 5; a->y = 997; a->str = "Irgendwas"; return a; } void print_a( A *v ) { printf("x = %d\n", v->x); printf("y = %d\n", v->y); printf("str = %s\n", v->str); } |
Bei der Umwandlung des C-String-Zeigers in einen Fortran-Zeiger ist die genaue Stringlänge erforderlich. Ist diese zu klein gewählt, so ist nur ein Teil des C-Strings über den Fortran-Zeiger sichtbar. Wird diese Stringlänge zu groß gewählt, dann wird diese Länge auch voll ausgenutzt und nach dem eigentlich gewünschten String folgen noch ein Menge x-beliebige Zeichen, da in Fortran der C-String-Begrenzer \0
nicht die Bedeutung wie in C besitzt.
Im obigen Beispiel wurde die Speicherplatzreservierung und Wertebelegung für eine Variable des Typs A
in einer C-Funktion erledigt. Nun soll diese Aufgabe im Fortran-Programm wahrgenommen werden und dann diese in Fortran mit Werten belegte Variable an die print_a
-Funktion übergeben werden. Kein Problem, möchte man im ersten Augenblick meinen. Doch der c_ptr
-Typ im Datenverbund für den Zeiger auf eine Zeichenkette macht Probleme. Der g95-Compiler duldet bspw. in der c_loc
-Funktion keine Zeichenketten mit einer Länge größer als 1. Der Compiler "castet" auch nicht von selbst im Datenverbundkonstruktor eine Zeichenkette in den Typ c_ptr
. Eine Möglichkeit, diese Probleme zu umgehen, besteht darin, einfach einer C-Funktion einen String zu übergeben und die Adresse dieses Strings zurückgeben zu lassen. Diese Variante wird auch im folgenden Beispiel verwendet.
Beispiel: Struktur als Rückgabewert und Argument einer Funktion - Teil 3
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003 (oder neuer)-Code |
module test use, intrinsic :: iso_c_binding implicit none type, bind( C ) :: A integer( c_int ) :: xc, yc type( c_ptr ) :: str end type type( A ), target :: a1 ! bei Verwendung des g95 könnte diese Var.deklaration ! auch im Hauptprogramm als lokale Variable ! vorgenommen werden. Der Sun-Compiler erlaubt ! kein gleichzeitiges C-Binding mit einer lokalen Var. interface subroutine print_a( x ) bind( c ) use, intrinsic :: iso_c_binding type( c_ptr ), value :: x end subroutine print_a function c_string_addr( s ) bind( c ) use, intrinsic :: iso_c_binding character( c_char ) :: s type( c_ptr ) :: c_string_addr end function c_string_addr end interface end module test program main use test implicit none character( len = 30 ) :: str str = "Das ist ein Beispiel" // C_NULL_CHAR a1 = A( 14, 56, c_string_addr( str ) ) call print_a( c_loc( a1 ) ) ! Ausgabe: ! x = 14 ! y = 56 ! str = Das ist ein Beispiel end program main |
Programmcode |
#include <stdlib.h> #include <stdio.h> typedef struct { int x; int y; const char *str; } A; void print_a( A *v ) { printf("x = %d\n", v->x); printf("y = %d\n", v->y); printf("str = %s\n", v->str); } char *c_string_addr( char *str ) { return str; } |
Problematische Kamelhöckerschreibweise?
[Bearbeiten]C ist case-sensitive, Fortran nicht. Oft sind Funktionen in C-Bibliotheken in der Kamelhöckerschreibweise vorzufinden, z.B.
void writeHallo();
Die Einbindung dieser C-Funktion in ein Fortran-Programm mit
interface subroutine writeHallo() bind(c) end subroutine writeHallo end interface
könnte beim Linken eine Fehlermeldung der Art
undefined reference to `writehallo'
liefern.
Was ist passiert? Wie schon angedeutet, ist Fortran case-insensitive. Für einen Fortran-Compiler ist writeHallo
gleich writehallo
oder WRITEHaLLO
. Für einen C-Compiler wären das alles unterschiedliche Funktionsbezeichner. Also wandelt der Fortran-Compiler die "unnütze" Groß-/Kleinschreibung in eine einheitliche Schreibweise um und der C-Compiler nicht. Dementsprechend findet der Linker auch keine Funktion writehallo
. Es gibt eben nur die nicht-idente C-Funktion writeHallo
.
Aber auch für dieses Problem gibt es in Fortran eine Lösung. Mit dem bind
-Attribut können zusätzlich auch noch Labels (Benennungen) vergeben werden, z.B.:
bind( c, name="xyz" )
Solche Labels in Stringform sind nicht von der "Kopf ab"-Strategie des Fortran-Compilers betroffen. Das ist keine C-Binding-Spezialität, sondern trifft generell für alle Zeichenketten zu. Ein
interface subroutine writeHallo() bind(c, name="writeHallo") end subroutine writeHallo end interface
sollte das beschriebene Link-Problem lösen.
Sonstiges
[Bearbeiten]- Auch C-Funktionszeiger kennt das
iso_c_binding
-Modul. Zu diesem Zwecke gibt es den Datenverbundc_funptr
, einige Umwandlungsfunktionen ähnlich jenen im Abschnitt Zeiger behandelten und die KonstanteC_NULL_FUNPTR
.
<<< zur Fortran-Startseite | |
<< Fortran und C | F >> |
< Fortran 90/95 und C | Fortran 2003 und C > |