Fortran接受来自C的字符串(?) [英] Fortran accepting string(?) from C

查看:241
本文介绍了Fortran接受来自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;



froutine.f90:


  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 routine 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;
}

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 interfaces 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆