2011-04-10 86 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