我觉得这应该是一个简单的问题,但我无法让它发挥作用。我有一些Fortran代码,它接受如下输入:
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,& NAME [0],一个被剥夺了\ 0的字符名称[4]以完全符合名称,并且它们都返回显示相同的空指针。有人知道如何从C ++中将函数名称转换为Fortran中的EXTERNAL变量吗?
谢谢
答案 0 :(得分:12)
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
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:
下运行它 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
答案 1 :(得分:3)
似乎FORTRAN 77需要指向字符和的指针,字符串的长度可以从C ++(或C)传递到FORTRAN。
请参阅this utah.edu document on using C and C++ with FORTRAN,并专门搜索以“CHARACTER * n arguments”开头的文档部分。
答案 2 :(得分:2)
与支持表达式的反射和/或运行时评估的更多动态语言(如python)相反,Fortran,C和C ++不支持。也就是说,没有内置方法将包含过程名称的字符串转换为过程引用并调用它。
也就是说,在您的示例中,NAME需要是指向函数的指针,而不是字符串。通过使用ISO_C_BINDING功能,您可以在C和Fortran之间传递函数指针。
答案 3 :(得分: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