2011-04-10 95 views
3

我覺得這應該是一個簡單的問題,但我無法讓它工作。我有一些Fortran代碼,其採用輸入,如:Fortran接受字符串(?)from C

 SUBROUTINE TRACE(X,Y,NAME,XX,YY) 
     EXTERNAL NAME 
     CALL NAME(X,Y,XX,YY) 

,我想在一個名字++通過由C形式:

float x,y,xx,yy; 
char * name="IGRF"; 
trace_(&x,&y,name,&xx,&yy); 

它編譯,但我總是得到段錯誤時我嘗試調用NAME子例程。在文件中定義了一個名爲IGRF的子例程,我可以直接從C++調用IGRF子例程,但需要此TRACE例程。在gdb中運行時,它表示NAME變量作爲void指針出現。

我試過路過名稱,& NAME,& NAME [0],一個char NAME [4]這是剝奪了其\ 0完全適合的名字,他們都回來顯示相同​​的空指針。有人知道如何從C++中獲取函數名稱到Fortran中的EXTERNAL變量中嗎?

謝謝

+0

什麼你在做這個平臺嗎? – EvilTeach 2011-04-10 03:10:27

+0

在Fedora 14(內核2.6.35.10-74)筆記本電腦上運行這個,用f95編譯(也嘗試過gfortran)和g ++ 4.5.1 – vityav 2011-04-10 04:35:45

回答

11

因此Fortran2003及其後續版本的一個優點是C互操作性爲定義爲成爲標準;這是一個PITA的使用,但一旦完成,它將保證跨平臺和編譯器工作。

所以這裏的cprogram.c,來調用Fortran程序getstring

#include <stdio.h> 

int main(int argc, char **argv) { 
    int l; 
    char *name="IGRF"; 

    l = getstring(name); 

    printf("In C: l = %d\n",l); 

    return 0; 
} 

和這裏的fortranroutine.f90

integer(kind=c_int) function getstring(instr) bind(C,name='getstring') 
    use, intrinsic :: iso_c_binding 
    character(kind=c_char), dimension(*), intent(IN) :: instr 
    integer :: len 
    integer :: i 

    len=0 
    do 
     if (instr(len+1) == C_NULL_CHAR) exit 
     len = len + 1 
    end do 


    print *, 'In Fortran:' 
    print *, 'Got string: ', (instr(i),i=1,len) 
    getstring = len 
end function getstring 

生成文件很簡單:

CC=gcc 
FC=gfortran 

cprogram: cprogram.o fortranroutine.o 
    $(CC) -o cprogram cprogram.o fortranroutine.o -lgfortran 

fortranroutine.o: fortranroutine.f90 
    $(FC) -c $^ 

clean: 
    rm -f *.o cprogram *~ 

並運行它的工作原理,在gcc/gfortran和icc/ifort下:

In Fortran: 
Got string: IGRF 
In C: l = 4 

更新:哦,我只是意識到你正在做的,而不是僅僅傳遞字符串更復雜;你基本上試圖傳遞一個指向C回調例程的函數指針。這是一個小竅門,因爲你必須使用Fortran interface來聲明C例程 - 只是使用extern將不起作用(因爲不存在類型檢查等問題,所以不如使用顯式接口)。應工作:

cprogram.c:

#include <stdio.h> 

/* fortran routine prototype*/ 
int getstring(char *name, int (*)(int)); 

int square(int i) { 
    printf("In C called from Fortran:, "); 
    printf("%d squared is %d!\n",i,i*i); 
    return i*i; 
} 


int cube(int i) { 
    printf("In C called from Fortran:, "); 
    printf("%d cubed is %d!\n",i,i*i*i); 
    return i*i*i; 
} 

int main(int argc, char **argv) { 
    int l; 
    char *name="IGRF"; 

    l = getstring(name, &square); 
    printf("In C: l = %d\n",l); 
    l = getstring(name, &cube); 
    printf("In C: l = %d\n",l); 


    return 0; 
} 

froutine。F90:

integer(kind=c_int) function getstring(str,func) bind(C,name='getstring') 
    use, intrinsic :: iso_c_binding 
    implicit none 
    character(kind=c_char), dimension(*), intent(in) :: str 
    type(c_funptr), value :: func 

    integer :: length 
    integer :: i 

    ! prototype for the C function; take a c_int, return a c_int 
    interface 
     integer (kind=c_int) function croutine(inint) bind(C) 
      use, intrinsic :: iso_c_binding 
      implicit none 
      integer(kind=c_int), value :: inint 
     end function croutine 
    end interface 
    procedure(croutine), pointer :: cfun 

    integer(kind=c_int) :: clen 

    ! convert C to fortran procedure pointer, 
    ! that matches the prototype called "croutine" 
    call c_f_procpointer(func, cfun) 

    ! find string length 
    length=0 
    do 
     if (str(length+1) == C_NULL_CHAR) exit 
     length = length + 1 
    end do 

    print *, 'In Fortran, got string: ', (str(i),i=1,length), '(',length,').' 

    print *, 'In Fortran, calling C function and passing length' 
    clen = length 
    getstring = cfun(clen) 

end function getstring 

和結果:

$ gcc -g -Wall -c -o cprogram.o cprogram.c 
$ gfortran -c fortranroutine.f90 -g -Wall 
$ gcc -o cprogram cprogram.o fortranroutine.o -lgfortran -g -Wall 
$ gpc-f103n084-$ ./cprogram 
./cprogram 
In Fortran, got string: IGRF(   4). 
In Fortran, calling C function and passing length 
In C called from Fortran:, 4 squared is 16! 
In C: l = 16 
In Fortran, got string: IGRF(   4). 
In Fortran, calling C function and passing length 
In C called from Fortran:, 4 cubed is 64! 
In C: l = 64 
+0

儘管我非常感謝所有的努力,但不幸的是我有幾萬行F77代碼,我試圖從C運行。是否很容易將其轉換爲Fortran2003?另外,我並不試圖從fortran中調用C例程,而是在同一個文件中調用其他fortran子例程。我只需要從C調用第一個函數,並給它下一個要調用的函數的名稱。 – vityav 2011-04-10 17:53:41

+1

你可以在現代fortran中編寫這個程序,然後連接其餘的程序。至於調用Fortran函數 - 就像janneb所說的那樣,Fortran運行時沒有什麼神奇的方法將字符串轉換爲函數指針。 (如果字符串不對應一個名字,它會怎麼做?)。看起來像F77這樣做的東西實際上只是僞裝了一個函數指針。如果你想這樣做,到目前爲止,最簡單的方法就是接受字符串(或者,就此而言,只傳遞一個整數),並使用if語句/ select case語句根據輸入字符串調用正確的子例程。 – 2011-04-10 18:03:20

+0

你的建議只是在它自己的例程中的case語句中完成,謝謝。 – vityav 2011-04-10 19:47:02

3

似乎FORTRAN 77既需要一個指向字符串的長度被從C++(或C)通入FORTRAN字符

請參閱this utah.edu document on using C and C++ with FORTRAN並專門搜索以「CHARACTER * n arguments」開頭的文檔部分。

+0

如果我在聲明外部變量之前嘗試修改變量NAME,它會抱怨更改尺寸嘗試第一個解決方案,使其成爲一個整數)或者只是沒有任何影響(在聲明它爲EXTERNAL之前聲明它爲CHARACTER *(*)NAME)。之後我立即添加了一個變量,用於定義長度,但這也沒有影響。試圖打印NAME(1)也segfaults。 – vityav 2011-04-10 04:48:01

+2

從2001年起鏈接到的網頁已過時幷包含錯誤。例如,「因此,目前沒有用於計算機編程語言之間通信的國際協議,而且其中一個不太可能開發。」不,有Jonathan Dursi描述的Fortran 2003的ISO C綁定和許多此前的答案。 ISO C綁定爲連接C和Fortran提供了一種標準且便攜的方式,這比以前需要編譯器和操作系統相關的黑客要好得多。 – 2011-04-10 06:09:45

2

與之相對更動態的語言,如支持表達式的反射和/或運行時評估蟒,FORTRAN,C,和C++沒有。也就是說,沒有內置的方法將包含過程名稱的字符串轉換爲過程引用並調用它。

也就是說,在您的示例中,NAME需要是指向函數的指針,而不是字符串。通過使用ISO_C_BINDING功能,您可以在C和Fortran之間傳遞函數指針。

+0

在嘗試添加C片段之前,代碼工作的方式是函數會調用此TRACE函數並傳入另一個要執行的子例程的名稱。你是說fortran自然會把它當作一個指針,並且我需要得到一個指向fortran子例程的C函數指針? – vityav 2011-04-10 17:57:18

+0

是的,在F2003中的「正確」過程指針之前,Fortran允許有限種類的「指向函數的指針」(在C語言中),您可以將過程作爲參數傳遞給其他過程,但不會將其存儲爲變量。所以你傳遞一個過程的地址,而不是包含過程名稱的字符串。有關如何使用ISO_C_BINDING執行此操作的詳細信息,請參閱Jonathan Dursi的更新答案。 – janneb 2011-04-10 19:43:12

0

我和CMake一起工作。

的CMakeLists.txt:

cmake_minimum_required(VERSION 3.5) 
project(CppFortran C CXX Fortran) 

add_executable(CppFortran 
    froutine.f90 
    main.cpp 
    ) 

的main.cpp

#include <iostream> 

extern "C" { 
int getString(char *file_name); 
} 

int main() { 
    int l; 
    char *name = (char*)"IGRF"; 

    l = getString(name); 
    std::cout << "In C++:"<< std::endl; 
    std::cout << "length: " << l << std::endl; 

    return 0; 
} 

froutine.f90

integer(kind=c_int) function getString(instr) bind(C,name='getString') 
    use, intrinsic :: iso_c_binding 
    character(kind=c_char), dimension(*), intent(IN) :: instr 
    integer :: len 
    integer :: i 

    len=0 
    do 
     if (instr(len+1) == C_NULL_CHAR) exit 
     len = len + 1 
    end do 

    print *, 'In Fortran:' 
    print *, 'Got string: ', (instr(i),i=1,len) 
    getstring = len 
end function getString 

控制檯輸出:

In Fortran: 
Got string: IGRF 
In C++: 
length: 4