【问题标题】:minimum modification to make a visual studio FORTRAN-C mixed code gfortran-gcc compatible使 Visual Studio FORTRAN-C 混合代码 gfortran-gcc 兼容的最小修改
【发布时间】:2018-12-02 23:27:20
【问题描述】:

我正在尝试修改 this code (gist as back up) 以使其与 gfortran-gcc 兼容。

  • 我删除了[VALUE] 标签
  • POINTER-fcray-pointer 标志一起用于gfortran,而不是[REFERENCE] 标记
  • 删除了__stdcall ,因为尝试了导致warning: ‘stdcall’ attribute ignored [-Wattributes]#define __stdcall __attribute__((stdcall)) 现在这就是我所拥有的:

C码CMAIN.C:

#include <stdio.h>

extern int FACT_(int n);
extern void PYTHAGORAS_(float a, float b, float *c);

main()
{
    float c;
    printf("Factorial of 7 is: %d\n", FACT_(7));
    PYTHAGORAS_(30, 40, &c);
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);
}

FORTRAN 代码FORSUBS.FOR:

  INTEGER*4 FUNCTION Fact (n)
  INTEGER*4 n 
  INTEGER*4 i, amt
  amt = 1
  DO i = 1, n
    amt = amt * i
  END DO
  Fact = amt
  END

  SUBROUTINE Pythagoras (a, b, cp)
  REAL*4 a
  REAL*4 b
  POINTER (cp, c)
  REAL*4 c
  c = SQRT (a * a + b * b)
  END

Makefile:

all:
    gfortran -c FORSUBS.FOR -fcray-pointer
    gcc -c CMAIN.C
    gfortran -o result.out FORSUBS.o CMAIN.o  
    rm -rf *.o

clean :
    rm -rf *.out *~ *.bak *.o

但我仍然收到错误:

CMAIN.o:在函数“main”中:

CMAIN.C:(.text+0x1d): 对 `FACT_(int)' 的未定义引用

CMAIN.C:(.text+0x4c): 未定义引用 `PYTHAGORAS_(float, float, float*)'

如果您能帮助我了解,我将不胜感激:

  • 是什么问题,我该如何解决?
  • 修改原始代码以使 gcc-gfortran 与最小更改兼容的最佳方法是什么。

P.S.1. 也在Reddit 上分享。 P.S.2.操作系统和编译器规格与this question相同。

【问题讨论】:

  • 当我编译你的fortran,并在.o 上使用nm 时,我得到的符号是:fact_pythagoras_ 与你在@987654344 中声明的符号@ 文件(即它们是 小写 与您在 .c 文件中使用的 大写 大小写)
  • 名称修饰是编译器特定的。这就是我们在本世纪拥有fortran-iso-c-binding 的原因。这里有很多类似的问题。
  • 通过引用传递,因为我至少写了两次。只需删除不需要的[REFERENCE]。您应该更担心按值传递。
  • @Foad,很明显,您对 Fortran 的了解仍然非常原始,因为该语言的名称是 Fortran。 Fortran 已经有 3 年左右的时间了。
  • 您必须从您的 all: 目标中删除 rm -rf *.o 才能保留您的 .o 文件。执行nm FORSUBS.o CMAIN.o 将输出每个.o 的符号表。 FORSUBS.o 将具有 T [文本] 符号,CMAIN.o 将显示 U 类型符号。这些应该匹配。

标签: c fortran mixed-programming


【解决方案1】:

Fortran 通过引用传递变量,正如任何 Fortran 到 C 教程的第一句话中所述。所以你:

  1. 不能只删除[VALUE],您应该添加现代VALUE 属性或更改C 代码以传递指针。

  2. 不应在 [REFERENCE] 的 Fortran 端使用指针,而应将其删除。

  3. 为您的编译器使用实际名称修饰,这些天通常是subroutine_name_(附加下划线,名称为小写)或使用现代bind(C, name="binding_name") 属性。

没有现代 Fortran,但有 VALUE(它只是一个没有的 PITA):

  INTEGER*4 FUNCTION Fact (n)
  INTEGER*4, VALUE :: n 
  INTEGER*4 i, amt
  amt = 1
  DO i = 1, n
    amt = amt * i
  END DO
  Fact = amt
  END

  SUBROUTINE Pythagoras (a, b, c) bind(C)
  REAL*4, VALUE :: a
  REAL*4, VALUE :: b
  REAL*4 c
  c = SQRT (a * a + b * b)
  END

而不仅仅是将您的 C 名称更改为小写(pythagoras_fact_)... 使用 VALUE 属性,您无需介绍您在其他答案中看到的所有 C 临时文件。 bind(C)VALUE 正常工作所必需的。它将使您免于重写调用 Fortran 过程的代码。

为获得最佳现代体验,请使用bind(C,name="any_name_you_want") 设置准确的链接符号名称。

【讨论】:

  • “更改C代码以传递指针”所以我应该将74030放在一些变量中然后传递指针吗?
  • 查看编辑。在手机上编码需要时间。
  • @Vladimir,Fortran 没有指定如何传递变量。我可以作为参考、价值或凿在石碑上。
  • @evets 我很清楚这一点,而且我知道更多细节。但是,必须根据目标受众区分信息水平和正确性水平。老师总是撒谎。或者好吧...进行简化。如果没有这种特殊的简化,许多调用 Fortran 外部子程序(包括非常常用的包,如 BLAS 或 LAPACK)的 C 代码将无法工作。或者必须更新 Fortran 代码,这正是这个问题试图避免的——它想要 minimal 更改。
  • @evets 我在这里经常知道谁写得更多会获得更多支持,但实际上告诉某人少做点工作通常会更好。即使这意味着省略一些细节。
【解决方案2】:

除了我的顶级 cmets,Fortran 通过引用传递,因此您必须修改 .c.for 文件。

下面的代码有效。可能有一种更简单的方法来声明事物,但这应该[至少]让你走得更远。 警告:自从 Fortran IV 时代以来,我没有做太多的 fortran,所以我有点生疏了。我会遵从 Vladimir 的 VALUE 解决方案作为更好的选择。


#include <stdio.h>

#if 0
extern int fact_(int n);
extern void pythagoras_(float a, float b, float *c);
#else
extern int fact_(int *n);
extern void pythagoras_(float *a, float *b, float *c);
#endif

int
main(void)
{
    float c;
#if 0
    printf("Factorial of 7 is: %d\n", fact_(7));
#else
    int n = 7;
    printf("Factorial of 7 is: %d\n", fact_(&n));
#endif

#if 0
    pythagoras_(30, 40, &c);
#else
    float a = 30;
    float b = 40;
    pythagoras_(&a, &b, &c);
#endif
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);

    return 0;
}

    INTEGER*4 FUNCTION Fact (n)
    INTEGER*4 n
    INTEGER*4 i, amt
    amt = 1
    DO i = 1, n
        amt = amt * i
    END DO
    Fact = amt
    END

    SUBROUTINE Pythagoras (a, b, c)
    REAL*4 a
    REAL*4 b
    REAL*4 c
    c = SQRT (a * a + b * b)
    END

这是程序输出:

Factorial of 7 is: 5040
Hypotenuse if sides 30, 40 is: 50.000000

更新:

我从您的代码中得到相同的 undefined reference 错误!

啊哈!

我没有提到的一件事 [因为我认为这不会产生影响] 是我将源文件名更改为全部使用小写字母(例如 CMAIN.C --&gt; cmain.cFORSUBS.FOR --&gt; forsubs.for

这样,nm *.o 的输出会产生:

 cmain.o:
                 U fact_
0000000000000000 T main
                 U printf
                 U pythagoras_

forsubs.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

fortran 源文件名的更改无关紧要。但是,C 源文件名可以!

更重要的是文件名后缀(即.C更改为.c)。

这是因为gcc 将[尝试变得聪明并]查看后缀以确定文件是用哪种语言编写并相应地编译。例如,gcc -c foo.cpp 将编译文件,就好像它是用c++ 编写的,而不是 c,就好像命令是:g++ -c foo.cpp

虽然.cppc++ 文件名的[更多] 常用后缀,但c++ 文件的备用后缀是:.C

也就是说,大多数项目使用.cpp 约定,但有些使用.C 约定。首选.cpp 而不是.C 的原因之一是Windows 文件系统不区分大小写。所以,.C.c 看起来是一样的。但是,POSIX 系统(例如 linux、macOSX、iOS、android 等)具有区分大小写的文件名,因此可以使用任何一种约定。

因此,gcc -c CMAIN.C 将编译为 c++。这是c++ 对符号进行“名称修改”的样式——不是我们想要的。在c++ 中,进行了修改以允许“重载”函数名称。也就是说,两个 [或更多] 不同的函数可以具有相同的名称,只要它们使用不同的参数即可。例如:

void calc(int val);
void calc(int val1,int val2);
void calc(double fval);

如果我们使用CMAIN.C,这是nm *.o的输出:

 CMAIN.o:
0000000000000000 T main
                 U printf
                 U _Z11pythagoras_PfS_S_
                 U _Z5fact_Pi

FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

通过c++filt 运行那个 文件,以“分解”我们得到的c++ 名称:

 CMAIN.o:
0000000000000000 T main
                 U printf
                 U pythagoras_(float*, float*, float*)
                 U fact_(int*)

FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

因此,如果可能,请尝试使用小写文件名 [这是推荐的最佳做法]。但是,至少不要使用.C

【讨论】:

  • 我从你的代码中得到同样的undefined reference 错误!
  • 天哪!我从来没有想过我会被文件扩展名打败!为什么 gcc 会那样做?!!如果我想编译为 cpp,我会使用 g++!
  • @Foad 这不是 GCC 的工作方式。即使是 Fortran 源代码,您也可以调用 gcc。或 gfortran 在 C++ 源文件上。您使用的命令未选择正确的前端。
  • @VladimirF:是的,但是,该命令会告诉它尝试链接哪些库。所以仍然有充分的理由使用与源文件语言相匹配的命令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
相关资源
最近更新 更多