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( c[, name="c_func"] )
... 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.
  • use, intrinsic :: iso_c_binding
... Einbindung des intrinsischen Moduls iso_c_binding
  • real( kind = c_float )
... C-Datentyp float.
  • value
... 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 Datentyp character mit einem kind-Wert c_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 Datenverbund c_funptr, einige Umwandlungsfunktionen ähnlich jenen im Abschnitt Zeiger behandelten und die Konstante C_NULL_FUNPTR.



<<< zur Fortran-Startseite
<< Fortran und C F >>
< Fortran 90/95 und C Fortran 2003 und C >