Fortran程序中調(diào)用C++編寫的動態(tài)庫的問題分為兩個子問題
- Fortran調(diào)用c形式函數(shù)接口
- 為c++動態(tài)庫編寫c形式的函數(shù)接口
下面把踩坑過程進行分享
1. C++類庫編寫C形式接口
1.1 c與c++的函數(shù)符號名
c標(biāo)準(zhǔn)的函數(shù)名與編譯后的符號名一致(使用gcc
編譯的.c
文件中的函數(shù))宣羊,但c++的函數(shù)名與編譯后的符號名不一致(g++
編譯的.cpp
文件中的函數(shù))。未使c++的函數(shù)按照c標(biāo)準(zhǔn)編譯汰蜘,需要使用extern "C"
關(guān)鍵字仇冯。
那么,在c++的類庫中鉴扫,若希望某些函數(shù)表現(xiàn)出于c標(biāo)準(zhǔn)相同的接口形式赞枕,那么在頭文件中應(yīng)按照下面格式書寫。經(jīng)過__cplusplus
預(yù)編譯指令的包裝后坪创,該頭文件使其中聲明的函數(shù)在c炕婶,c++的編譯器中均按照c標(biāo)準(zhǔn)的函數(shù)形式表現(xiàn)。
// foo_and_bar.h
#ifdef __cplusplus
extern "C" {
#endif
void foo(int,);
void bar(double);
#ifdef __cplusplus
}
#endif
1.2 c++的類封裝
在c中沒有class
的概念莱预,為c++類庫編寫的c形式的函數(shù)接口中柠掂,不能直接使用class
聲明的類,那么需要為c++類聲明的頭文件中如下編寫依沮。
// moment.h
#ifndef MOMENT_H
#define MOMENT_H
#ifdef __cplusplus
class Moment
{
private:
int jd;
double js;
public:
Moment(int jd, double jd);
int get_jd() const;
double get_js() const;
};
#else
typedef struct Moment
Moment;
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern void moment_new(Moment* obj, int jd, double js);
extern int moment_get_jd(const Moment* obj);
extern double moment_get_js(const Moment* obj);
#ifdef __cplusplus
}
#endif
#endif
如此聲明的Moment
頭文件展示出了c與c++兩種形式的特性涯贞,在c++編譯器中Moment
類型的定義是完整的,既可以使用c++類操作使用Moment
危喉,也可以使用與c公用的函數(shù)方法接口moment_new()
,moment_get_jd()
,moment_get_js()
宋渔。
不過在c編譯器中,Moment
僅是一個代號辜限。僅可以按照moment_new()
,moment_get_jd()
,moment_get_js()
函數(shù)的接口形式操作皇拣。因為在c編譯器看來,Moment
僅是一個代號薄嫡,因此這三個函數(shù)中關(guān)于Moment
的參數(shù)必須是指針形式參數(shù)氧急。相關(guān)實現(xiàn)函數(shù)為
// moment.cpp
void moment_new(Moment* obj, int jd, double js)
{
obj= new Moment(jd, js);
}
int moment_get_jd(const Moment* obj) const
{
return obj->get_jd();
}
double moment_get_js(const Moment* obj) const
{
return obj->get_js();
}
2 Fortran調(diào)用c函數(shù)
2.1 Fortran調(diào)用函數(shù)規(guī)則
fortran編譯器,如gfortran毫深,基本相當(dāng)于c編譯器gcc的前端吩坝,經(jīng)gfortran解析后的fortran程序最終還是按照c的邏輯編譯。其中fortran的函數(shù)有一些特點:
- fortran中函數(shù)名不區(qū)分大小寫
foo
哑蔫,Foo
钉寝,FOO
是相同函數(shù) - fortran函數(shù)名對應(yīng)的c函數(shù)名后面加下劃線,即在fortran中調(diào)用了函數(shù)
foo()
闸迷,相當(dāng)于在c中調(diào)用函數(shù)foo_()
- fortran函數(shù)的參數(shù)均是按指針傳遞瘩蚪,即在fortran中調(diào)用函數(shù)
foo(INTEGER*4, REAL*8)
相當(dāng)于在c中調(diào)用函數(shù)foo_(int*, double*)
2.2 將c++類庫寫作Fortran可調(diào)用形式
以上述Moment類,調(diào)整其中的c部分接口函數(shù)形式
// moment.h
#ifndef MOMENT_H
#define MOMENT_H
#ifdef __cplusplus
class Moment
{
private:
int jd;
double js;
public:
Moment(int jd, double jd);
int get_jd() const;
double get_js() const;
};
#else
typedef struct Moment
Moment;
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern void moment_new_(Moment** obj, int* jd, double* js);
extern int moment_get_jd_(const Moment** obj);
extern double moment_get_js_(const Moment** obj);
#ifdef __cplusplus
}
#endif
#endif
主要的變化是稿黍,c形式接口函數(shù)moment_new()
,moment_get_jd()
,moment_get_js()
中所有參數(shù)增加一級直至疹瘦,關(guān)于Moment
的參數(shù)修改為二級指針,即指針的指針巡球,int
與double
修改為int*
與double*
言沐;此外為每個函數(shù)名后增加下劃線_
。對應(yīng)的實現(xiàn)為
// moment.cpp
void moment_new_(Moment** obj, int* jd, double* js)
{
*obj= new Moment(*jd, *js);
}
int moment_get_jd_(const Moment** obj) const
{
return *obj->get_jd();
}
double moment_get_js_(const Moment** obj) const
{
return *obj->get_js();
}
2.3 Fortran調(diào)用c接口形式的c++類庫
有如下要點:
-
type(c_ptr) :: foo
聲明相當(dāng)于c語言中聲明foo
的類型為void *
- 使用
type(c_ptr)
需要use iso_c_binding
語句 - 調(diào)用c接口外部函數(shù)酣栈,需要用
external
關(guān)鍵字聲明 - 若要使用
moment_get_jd()
與moment_get_js()
函數(shù)返回值险胰,必須先聲明
program main
use iso_c_binding
implicit none
type(c_ptr) :: foo
external moment_new
integer(4) moment_get_jd
external moment_get_jd
real(8) moment_get_js
external moment_get_js
integer(4) jd
real(8) js
call moment_new(foo, 1, 2d0)
jd = moment_get_jd(foo)
js = moment_get_js(foo)
write(*,*) jd, js
end
以Fortran的call moment_new(foo, 1, 2d0)
語句為例,其中foo
為void*
格式矿筝,1
與2d0
分別為INTEGER*4
與REAL*8
的常量起便,那么該語句的調(diào)用相當(dāng)于調(diào)用c函數(shù)moment_new_(void**, int*, double*)
,與我們?yōu)?code>Moment類編寫的接口函數(shù)extern void moment_new_(Moment** obj, int* jd, double* js)
匹配。
3 平臺兼容性至動態(tài)庫加載失敗
通常榆综,動態(tài)庫Windows下的.dll
Linux下的.so
文件找不到妙痹,加載失敗是因為動態(tài)庫路徑設(shè)置錯誤導(dǎo)致。一鼻疮、編譯階段怯伊,動態(tài)庫路徑直接使用-L{dynamic_library_path}
,-l{dynamic_library_name}
兩個選項指定判沟;二耿芹、進程加載與運行階段,動態(tài)庫由鏈接器搜索尋找挪哄,Linux下可以設(shè)置環(huán)境變量LD_LIBRARY_PATH
指定吧秕,Windows下可以將動態(tài)庫至于exe
文件相同文件夾下。
若c++類庫與fortran編譯器的平臺版本不同迹炼,也會導(dǎo)致動態(tài)庫文件找不到砸彬、加載失敗問題沪蓬。例如挑庶,新編寫的c++類庫使用的的64-bit編譯組件編譯的.dll
文件,而fortran使用的是 32-bit的gfortran
包蓝,那么在fortran編譯時則找不到動態(tài)庫文件咱扣,更進一步绽淘,找不到的是32-bit的C++動態(tài)庫