基础背景创建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版)
foo.h
#ifndef FOO_H_INCLUDED #define FOO_H_INCLUDED extern void foo(void); #endif // FOO_H_INCLUDED
foo.c
#includevoid 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命令详解
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 .tlsC/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
#includeUbuntu使用SO#include extern void sub(); int main() { sub(); return 0; }
参考:ubuntu下codeblocks生成so并使用
参考: 在 Linux 使用 GCC 编译C语言共享库
有些时候我们不想将所有构建命令都写到一个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介绍
参考:Mix C Fortran
参考:C++调用Fortran静态库中的函数
由于 GNU 的 Fortran 和 C 语言二者的函数彼此可以直接相互调用,所以混合编程可以非常容易地实现。只要你足够仔细,确保函数调用时传递的参数类型正确,函数就可以在两种语言间来回调用,就像它们是同一种语言一样。下表中列出了 Fortran 的数据类型和它们在 C 中对应的类型。这张表在大多数平台下是没问题的,但是或许会有例外的情况发生。在你打算传递某种数据类型时,先编写一个简单的例子进行测试将是很明智的。
由于 Fortran 总是以引用的方式传递参数,而 C 则始终以地址方式传递数组,因此数组做参数时不需做任何修改。但是对多维数组来说,混合调用时其下标需要翻转,因为 Fortran 的数组是以列为主序(column-major order)而 C 数组以行为主序(row-major order)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)