Fortran接受来自C的字符串(?) [英] Fortran accepting string(?) from C
问题描述
我觉得这应该是一个简单的问题,但我无法实现它的工作。我有一些Fortran代码需要输入:
SUBROUTINE TRACE(X,Y,NAME,XX,YY)
EXTERNAL NAME
CALL NAME(X,Y,XX,YY)
和I 'm试图从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和& NAME [0] ,一个字符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 \\\
,l);
返回0;
}
以下是 fortranroutine.f90
:
整数(kind = c_int)函数getstring(instr)bind(C,name ='getstring')
use,intrinsic :: iso_c_binding
character(kind = c_char),dimension(*),intent(IN):: instr
integer :: len
integer :: i $ b $如果(instr(len + 1)== C_NULL_CHAR)退出
len = len + 1
end do
$ b len = 0
do
b'b
print *,'在Fortran中:'
print *,'Got string:',(instr(i),i = 1,len)
getstring = len
结束函数getstring
makefile非常简单:
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下运行它:
$ b
在Fortran中:
字符串:IGRF
在C:l = 4
更新:哦,我刚刚意识到你正在做的事情比传递一个字符串更精细;你基本上试图传递一个指向C回调例程的函数指针。这是一个小窍门,因为你必须使用Fortran interface
来声明C例程 - 只是使用extern不起作用(并且不如显式接口无论如何,因为没有类型检查等)所以这应该工作:
cprogram.c:
#include< stdio.h>
/ * fortran例程原型* /
int getstring(char * name,int(*)(int));
int square(int i){
printf(In C called from Fortran :,);
printf(%d平方是%d!\\\
,i,i * i);
返回i * i;
int cube(int i){
printf(In C called from Fortran :,);
printf(%d cubed is%d!\\\
,i,i * i * i);
返回i * i * i;
int main(int argc,char ** argv){
int l;
char * name =IGRF;
l = getstring(name,& square);
printf(In C:l =%d \\\
,l);
l = getstring(name,& cube);
printf(In C:l =%d \\\
,l);
return 0;
integer(kind = c_int)函数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
! C函数的原型;取一个c_int,返回一个c_int
接口
整数(kind = c_int)function croutine(inint)bind(C)
use,intrinsic :: iso_c_binding
implicit none
整数(kind = c_int),value :: inint
结束函数croutine
结束接口
过程(croutine),指针:: cfun
整数(kind = c_int):: clen
!将C转换为fortran程序指针
!与称为croutine的原型匹配
调用c_f_procpointer(func,cfun)
!查找字符串长度
length = 0
do
if(str(length + 1)== C_NULL_CHAR)exit
length = length + 1
end do
print *,'在Fortran中,得到了string:',(str(i),i = 1,length),'(',length,')。'
print * ,'在Fortran中调用C函数并传递长度'
clen = length
getstring = cfun(clen)
结束函数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
在Fortran中,得到字符串:IGRF(4)。
在Fortran中,调用C函数并传递长度
在Fortran中调用的C中:4平方为16!
在C中:l = 16
在Fortran中,得到字符串:IGRF(4)。
在Fortran中,调用C函数并传递长度
在从Fortran中调用的C中:4立方体为64!
在C:l = 64
I feel like this should be an easy question, but I can't get it to work. I have some Fortran code that takes an input like:
SUBROUTINE TRACE(X,Y,NAME,XX,YY) EXTERNAL NAME CALL NAME(X,Y,XX,YY)
and I'm trying to pass in a name from C++ in the form:
float x,y,xx,yy; char * name="IGRF"; trace_(&x,&y,name,&xx,&yy);
It compiles, but I always get segfaults when I try to call the NAME subroutine. A subroutine called IGRF is defined in the file, and I can call the IGRF subroutine directly from C++, but need this TRACE routine. When running in gdb, it says the NAME variable comes through as a pointer to void.
I've tried passing NAME, &NAME, &NAME[0], a char NAME[4] that's stripped of its \0 to perfectly fit the name, and they all come back showing the same void pointer. Does anybody know how to get a function name from C++ into that EXTERNAL variable in Fortran?
Thank you
解决方案So one advantage of Fortran2003 and later is that C interoperability is defined into the standard; it's a bit of a PITA to use, but once it's done, it's guaranteed to work across platforms and compilers.
So here's
cprogram.c
, calling a Fortran routinegetstring
:#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; }
and here's
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
The makefile is simple enough:
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 *~
and running it works, under both gcc/gfortran and icc/ifort:
In Fortran: Got string: IGRF In C: l = 4
Update: Oh, I just realized that what you're doing is rather more elaborate than just passing a string; you're essentially trying to pass a function pointer pointing to a C callback routine. That's a little tricker, because you have to use Fortran
interface
s to declare the C routine -- just using extern won't work (and isn't as good as explicit interfaces anyway, as there's no type checking, etc.) So this should work: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
And the results:
$ 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
这篇关于Fortran接受来自C的字符串(?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!