gfortran创建Fortran的动态库供C++调用

gfortran创建Fortran的动态库供C++调用,第1张

gfortran创建Fortran的动态库供C++调用

文章目录

基础背景创建Fortran动态库C代码导出DLL语法C代码导出SO语法Fortran代码导出DLL语法如何查看生成的DLL内函数

UbuntuWindows10 C/C++调用Fortran的DLLUbuntu使用SOCMAKE如何多CMakeLists.txt构建项目[混编] C++调用Fortran静态库中的函数

基础背景

目的:备忘录性质

工具:Code::Blokcs16.01+MinGW

参考:Code blocks 编译Fortran(转载)

学习如何使用CB编译Fortran控制台应用

创建Fortran动态库

如果直接运行DLL工程,会显示You must select a host application to “run” a libary…好在windows本身提供了一个完美的测试dll


subroutine sub()
  implicit none
  write(*,*) "This is subroutine"
  return
end

参考: Codeblocks如何调试C++生成的DLL

C代码导出DLL语法

参考:Codeblocks创建和调用DLL动态链接库(C语言)

simple.h

#ifndef SIMPLE_H_INCLUDED
#define SIMPLE_H_INCLUDED

#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

int DLL_EXPORT get_id(void);
int DLL_EXPORT add(int,int);

#ifdef __cplusplus
}
#endif

#endif // SIMPLE_H_INCLUDED

simple.c

#include "simple.h"
int DLL_EXPORT get_id(void)
{
    return 10;
}

int DLL_EXPORT add(int x,int y)
{
    return x+y;
}

点击build可得到三个文件如下:

Demo-CB-C-DLL.dlllibDemo-CB-C-DLL.alibDemo-CB-C-DLL.def

再建一个工程调用dll的工程:

通过“激活工程”菜单项来实现多工程项目的“同时”管理

main.c(隐式调用)

关键点: Project build options => linking settings => link Library => add 我们需要调用的DLL对应的.a文件

#include 
#include 

#include "simple.h"

int main()
{
    printf("id = %dn",  get_id() );
    printf("id = %dn",  add(1,2) );
    system("pause");
    return 0;
}


main.c(显示调用)

#include 
#include 

//#include "simple.h"

#include 
typedef int(*lpGet_id)(void); //定义函数类型
typedef int(*lpAdd)(int,int); //定义函数类型
HINSTANCE hDll; //DLL句柄
lpGet_id get_id;
lpAdd add;

int main()
{
    hDll = LoadLibrary("Demo-CB-C-DLL.dll"); //加载 dll
    get_id = (lpGet_id)GetProcAddress(hDll, "get_id");//通过指针获取函数方法
    add = (lpAdd)GetProcAddress(hDll, "add");//通过指针获取函数方法
    printf("id = %dn",  get_id() );//调用函数
    printf("id = %dn",  add(1,2) );//调用函数
    FreeLibrary(hDll);//释放Dll句柄
    system("pause");
    return 0;
}

不难发现:显示调用无需添加.a文件!
参考:Windows环境下创建并使用动态链接库(CodeBlocks版)

C代码导出SO语法

foo.h

#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED

extern void foo(void);

#endif // FOO_H_INCLUDED

foo.c

#include 

void foo(void)
{
    puts("Hello, I'm a shared library");
}

main.c

#include 
#include 
#include "foo.h"

int main()
{
    foo();
    return 0;
}

注意:linker要添加libfoo.a到工程里

Fortran代码导出DLL语法

自下转载:

!DEC$ ATTRIBUTES DLLEXPORT::函数
在我们常规的语法学习中,! 表示注释,是无效的。语法规则中也是如此规定的。但是你要注意,编译器是独立的,每一个编译器都可以按照自己的意愿来实现代码的转换。之所以大家都遵循语法的规定,是为了形成一个规范,以便程序员可以书写一次代码,而可以使用不同的编译器完成。其实这正如中文英语一样。人们提倡说普通话。但也会有方言。!DEC$ 正是这样的方言。因为语法中没有对 DLL 的任何规定。(因为只有windows有DLL,而其他 *** 作系统没有,所以语法是不会规定的。)IVF 为了能实现语法中没有规定的内容,就自己增加了这样一个“方言”。用 !DEC$ 表示这一系列语法未规定的内容。此时,它并非表示注释的,无效的。对 IVF 来说,!DEC$ 是有效的语句!!!之所以以 ! 开头,是IVF希望这样的代码能够被其他编译器忽略,这样不至于到另一个编译器下,出现语法错误。(另一个编译器不支持 !DEC$ 这样的用法,把它视为注释,这样虽然实现不了 DLL 导出的功能,但也不至于编译通不过)实际上,!DEC$ 的用法也不是 Intel 开创的。而是 DEC,Digital Enquipment Company。这个公司开发过 IVF 的前身:DVF。于是开创了这样属于自己的用法。Intel 为了让程序员们的代码能够保持一致性,沿用了 DEC 公司的用法。

参考:fortran生成dll给python调用
参考:c++动态函数库的创建与调用(.def)
参考:C++项目中的extern “C” {}
参考:反汇编objdump命令详解

如何查看生成的DLL内函数 Ubuntu

Ubuntu下查看so文件的函数列表,可使用如下命令:

nm -sD XXX.so
objdump -tT  XXX.so
nm libcyusb.so | grep "usb_init"

参考: linux中的nm命令简介

Windows10

请下载:dumpbin.exe (含使用说明)
请注意:__declspec用法总结(Microsoft c++)
或者参考:几款查看dll和exe信息的小工具
通常我们只关心“ordinal hint RVA name”下面的内容:

❯ DUMPBIN.EXE /EXPORTS .Demo-CB-Fortran-Dll.dll
Microsoft (R) COFF Binary File Dumper Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file .Demo-CB-Fortran-Dll.dll

File Type: DLL

  Section contains the following exports for Demo-CB-Fortran-Dll.dll

           0 characteristics
    61F52EF1 time date stamp Sat Jan 29 20:11:29 2022
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00001280 sub_

  Summary

        1000 .CRT
        1000 .bss
        1000 .data
        1000 .debug_abbrev
        1000 .debug_aranges
        2000 .debug_info
        1000 .debug_line
        1000 .edata
        1000 .eh_frame
        1000 .idata
        1000 .rdata
        1000 .reloc
        1000 .text
        1000 .tls
C/C++调用Fortran的DLL

foo.f95

! A fortran95 lib program for G95
subroutine sub()
  implicit none
  write(*,*) "This is subroutine"
  return
end

main.c

#include 
#include 
extern void sub_();
int main()
{
    sub_();
    return 0;
}

注意:导出的DLL中的函数名与源码中大概率不一样。不修改fortran的话,就修改C++的符号名,但是这可能因为不同编译器的符号修饰不同,而导致代码不通用。根据 nm 获得符号名。再对应修改 cpp 代码即可。

不难发现:不像C的DLL,Fortran的DLL没有头文件,为了让编译器找到DLL内的函数,我们必须将相应的.a文件告诉给连接器哦!

如果使用bind关键字配置函数符号名(可能出现符号名重复冲突,故推荐将名字弄的复杂些):
foo.f95

! A fortran95 lib program for G95
subroutine sub() bind(C,NAME="sub")
  implicit none
  write(*,*) "This is subroutine"
  return
end

main.c

#include 
#include 
extern void sub();
int main()
{
    sub();
    return 0;
}
Ubuntu使用SO

参考:ubuntu下codeblocks生成so并使用
参考: 在 Linux 使用 GCC 编译C语言共享库

CMAKE如何多CMakeLists.txt构建项目

有些时候我们不想将所有构建命令都写到一个CMakeLists.txt文件中去,而是将多个构建目标分配到几个CMakeLists.txt文件中去,然后在一个总的文件中去调用其它子的文件。比如子模块放在独立的子目录中,子目录下有CMakeLists.txt,可将子模块编译成库。例如,我们将某个子目录下的Fortran代码编译成动态库,然后供主程序调用。那么该如何让cmake知道需要构建的其它目标的CMakeLists.txt的位置呢?答案是add_subdirectory命令。

命令格式
add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL])
添加一个子目录并构建该子目录。

命令解析

source_dir
必选参数。该参数指定一个子目录,子目录下应该包含CMakeLists.txt文件和代码文件。子目录可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前目录的一个相对路径。binary_dir
可选参数。该参数指定一个目录,用于存放输出文件。可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前输出目录的一个相对路径。如果该参数没有指定,则默认的输出目录使用source_dir。EXCLUDE_FROM_ALL
可选参数。当指定了该参数,则子目录下的目标不会被父目录下的目标文件包含进去,父目录的CMakeLists.txt不会构建子目录的目标文件,必须在子目录下显式去构建。例外情况:当父目录的目标依赖于子目录的目标,则子目录的目标仍然会被构建出来以满足依赖关系(例如使用了target_link_libraries)。

参考:CMAKE文档:add_subdirectory
参考:Cmake命令之add_subdirectory介绍

[混编] C++调用Fortran静态库中的函数

参考:Mix C Fortran
参考:C++调用Fortran静态库中的函数

由于 GNU 的 Fortran 和 C 语言二者的函数彼此可以直接相互调用,所以混合编程可以非常容易地实现。只要你足够仔细,确保函数调用时传递的参数类型正确,函数就可以在两种语言间来回调用,就像它们是同一种语言一样。下表中列出了 Fortran 的数据类型和它们在 C 中对应的类型。这张表在大多数平台下是没问题的,但是或许会有例外的情况发生。在你打算传递某种数据类型时,先编写一个简单的例子进行测试将是很明智的。
由于 Fortran 总是以引用的方式传递参数,而 C 则始终以地址方式传递数组,因此数组做参数时不需做任何修改。但是对多维数组来说,混合调用时其下标需要翻转,因为 Fortran 的数组是以列为主序(column-major order)而 C 数组以行为主序(row-major order)。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5718443.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存