Zum Inhalt springen

Fortran: Fortran und C: Fortran 2003

Aus Wikibooks
<<< 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 >