Fortran接受字符串(?)from C
我觉得这应该是一个简单的问题,但我无法让它工作。我有一些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变量中吗?
谢谢
因此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
尽管我非常感谢所有的努力,但不幸的是我有几万行F77代码,我试图从C运行。是否很容易将其转换为Fortran2003?另外,我并不试图从fortran中调用C例程,而是在同一个文件中调用其他fortran子例程。我只需要从C调用第一个函数,并给它下一个要调用的函数的名称。 – vityav 2011-04-10 17:53:41
你可以在现代fortran中编写这个程序,然后连接其余的程序。至于调用Fortran函数 - 就像janneb所说的那样,Fortran运行时没有什么神奇的方法将字符串转换为函数指针。 (如果字符串不对应一个名字,它会怎么做?)。看起来像F77这样做的东西实际上只是伪装了一个函数指针。如果你想这样做,到目前为止,最简单的方法就是接受字符串(或者,就此而言,只传递一个整数),并使用if语句/ select case语句根据输入字符串调用正确的子例程。 – 2011-04-10 18:03:20
你的建议只是在它自己的例程中的case语句中完成,谢谢。 – vityav 2011-04-10 19:47:02
似乎FORTRAN 77既需要一个指向字符串的长度被从C++(或C)通入FORTRAN字符和。
请参阅this utah.edu document on using C and C++ with FORTRAN并专门搜索以“CHARACTER * n arguments”开头的文档部分。
如果我在声明外部变量之前尝试修改变量NAME,它会抱怨更改尺寸尝试第一个解决方案,使其成为一个整数)或者只是没有任何影响(在声明它为EXTERNAL之前声明它为CHARACTER *(*)NAME)。之后我立即添加了一个变量,用于定义长度,但这也没有影响。试图打印NAME(1)也segfaults。 – vityav 2011-04-10 04:48:01
从2001年起链接到的网页已过时并包含错误。例如,“因此,目前没有用于计算机编程语言之间通信的国际协议,而且其中一个不太可能开发。”不,有Jonathan Dursi描述的Fortran 2003的ISO C绑定和许多此前的答案。 ISO C绑定为连接C和Fortran提供了一种标准且便携的方式,这比以前需要编译器和操作系统相关的黑客要好得多。 – 2011-04-10 06:09:45
与之相对更动态的语言,如支持表达式的反射和/或运行时评估蟒,FORTRAN,C,和C++没有。也就是说,没有内置的方法将包含过程名称的字符串转换为过程引用并调用它。
也就是说,在您的示例中,NAME需要是指向函数的指针,而不是字符串。通过使用ISO_C_BINDING功能,您可以在C和Fortran之间传递函数指针。
在尝试添加C片段之前,代码工作的方式是函数会调用此TRACE函数并传入另一个要执行的子例程的名称。你是说fortran自然会把它当作一个指针,并且我需要得到一个指向fortran子例程的C函数指针? – vityav 2011-04-10 17:57:18
是的,在F2003中的“正确”过程指针之前,Fortran允许有限种类的“指向函数的指针”(在C语言中),您可以将过程作为参数传递给其他过程,但不会将其存储为变量。所以你传递一个过程的地址,而不是包含过程名称的字符串。有关如何使用ISO_C_BINDING执行此操作的详细信息,请参阅Jonathan Dursi的更新答案。 – janneb 2011-04-10 19:43:12
我和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
什么你在做这个平台吗? – EvilTeach 2011-04-10 03:10:27
在Fedora 14(内核2.6.35.10-74)笔记本电脑上运行这个,用f95编译(也尝试过gfortran)和g ++ 4.5.1 – vityav 2011-04-10 04:35:45