Calling Fortran functions from C++ by means of shared libraries

本文介绍了一种通过共享库从C++调用Fortran函数的方法。这种方法允许将Fortran代码独立出来,使得C++开发者可以使用原生C++数据类型来调用Fortran函数。文章还探讨了不同类型数据(包括复杂数据类型、字符串、多维数组和函数指针)的传递,并提供了在Windows和Linux上的具体示例。

Refer to: http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2004/0401/0401weiguo/0401weiguo.htm

        Even though C++ is the programming language of choice for systems and business applications, Fortran still has a strong presence in scientific and engineering computation. In practice, programming in C++ and Fortran lets you make use of existing Fortran code bases, which are valuable resources and may significantly accelerate development.

        While it is possible to call Fortran functions from C++, C++ (unlike C) provides features that support passing by reference as arguments, complex data types, and the like. By using proper definitions of Fortran data types and functions in C++, you can compile and link Fortran functions with C++ code using C++ compilers. This technique has been discussed by Carsten Arnholm (http://home.online.no/~arnholm/cppf77.htm).

        In this article, I present another way to call Fortran functions in C++. This approach is to compile Fortran functions separately into shared libraries, then explicitly call them in C++ from the shared libraries. Of course, this method is not 100-percent portable — shared libraries compiled on one platform may not be used in another one. However, it is still portable because Linux and other operating systems support shared libraries, and there are system API functions available to make use of the shared libraries. The major advantage of this method is that Fortran code can be separated from C++ code, letting C++ developers use native C++ data types to call Fortran functions. And since there is no need for you to include additional header files, development and maintenance can also be separated. This increases the code modularity and reusability. Source code that implements this technique for both Windows and Linux is available at http://www.cuj.com/code/.

About the Data Types

        Fortran has a long history in scientific computation. Hitherto now, there exist several Fortran standards, known as Fortran 66/77/90/95/2000. Since most of the existing Fortran codes comply with Fortran 77 (F77) Standard, I'll only address calling of the F77 functions in C++ via shared libraries.

        Basically, there are six data types in F77 Standard, namely, INTEGER,REAL,DOUBLE PRECISION,COMPLEX,LOGICAL,CHARACTER [*n], wheren is the optional string length (in the range 1 to 32767). Most of these data types have their direct counterparts in basic C++ data types, except forCOMPLEX andCHARACTER.

        In C++, complex numbers are supported in the Standard Library rather than the core language. C++'s complex number class can be directly passed to F77 functions. Returning complex values from F77 functions may be different, since this is compiler-dependent behavior. (Here, I use GNU g77 as the F77 compiler.) However, C++'s complex class can also be directly used to hold the returned complex values from F77 functions.

        Because of the different ways of handling string lengths in F77 and C++, as well as the different ways of passing strings as function parameters,CHARACTER is another special data type. The C++CHARACTER class can also be directly passed to F77 functions. However, returningCHARACTER values from F77 functions is not supported by the method I present here.

        Fortran supports multidimensional arrays. Passing one-dimensional arrays is easy, and C++ pointers can be directly passed to Fortran functions. Multidimensional arrays, however, need special attention because Fortran stores array elements in a "column-first" fashion, while C++ uses "row-first" convention. Carsten Arnholm refers to a class calledFMATRIX, which can be used to convert a C++ matrix (two-dimensional array) to an F77 matrix. However, it also does not work in the method I present here. Instead, I treat multidimensional arrays as one-dimensional arrays; that is, one-dimensional C++ arrays are used and data of multidimensional arrays are rearranged to conform with the column-first rule. Then, the corresponding C++ pointers to the one-dimensional arrays are passed to call the F77 functions.

        Function pointers are another useful type of function arguments. This is particularly important in scientific and engineering computations. For instance, a function pointer is useful in the numerical integration routines where the algorithm can be implemented in a function, which takes the integrand and boundaries as the arguments. This is better than hard-coding the function with the algorithm. In F77, a function or subroutine can take another external function's name as an argument, and call that external function inside of itself. In C++, this can be done by using function pointers. It has been found that C++ function pointers can be directly passed to F77 functions.

DLLs and Shared Libraries

        Libraries are collections of object files, which can be statically or dynamically linked with the executable files. Dynamically linked libraries (DLLs) are one of the most important aspects of Windows. Shared libraries (or shared objects), on the other hand, are used in Linux and other UNIX-like operating systems. Neither DLLs nor shared libraries are native to C++ or F77--they are system-dependent techniques.

        There are basically two ways to link DLLs or shared libraries with executable files — implicit and explicit linking. Implicit linking is like static linking of libraries, except that implicit linking of shared libraries loads code upon startup. Explicit linking loads code while the program is running.

With explicit linking, programs do not need to know the name of the library upon startup, giving the program more flexibility. In this article, I'll show how to explicitly link F77 shared libraries in C++ programs.

        Explicit linking of DLLs on Windows can be performed by using the Windows API functionsLoadLibrary(),GetProcAddress(), andCloseLibrary{}. On Linux or Solaris (or perhaps other UNIX systems), explicit linking can be done via the API functionsdlopen(),dlsym(), anddlclose().

        In the following examples, I use the GNU Fortran g77 compiler to compile F77 files into shared libraries. On Windows XP Pro, I use g77 in MingW 2.0. The DLLfoo.dll is created fromfoo.for using the commands:

g77 -fno-f2c -shared -s -o foo.dll foo.for

        On Red Hat Linux 8.0, I use g77 in GCC 3.2 to create the shared libraries by using the same command options:

g77 -fno-f2c -shared -s -o foo.so foo.for

        C++ programs can then dynamically load the shared libraries. In the following examples, I use the freely available Borland command-line C++ compiler and g++ to compile the C++ programs on Windows and Linux, respectively.

Examples

        My first example illustrates how to pass a single argument to an F77 function.Listing 1 (ex1.for) is F77 code that defines a function with a REAL type input (x) and returns the value of2*x.Listing 2 is the C++ test file on Windows. The LoadLibrary() function dynamically loads ex1.dll, which was compiled from the F77 code. ex1.dll can be in the same directory as the C++ program, or any directory defined by thePATH environmental variable. Then GetProcAddress() finds the function to be run in the ex1.dll. Because of "name decoration," g77 adds an underscore after the name of the function in the shared library. Also, F77 function arguments are passed by reference; therefore, in ex1win.cpp (Listing 2) the float type argument is passed by reference to the function foo_(). After getting the address of the function, one can call the function as you would a normal function. Finally,FreeLibrary() unloads the DLL from the address space of the calling process.

         Listing 3 (ex1lnx.cpp) is the C++ file inListing 2 on Linux. You can create a shared library ex1.so from the F77 file ex1.for and put ex1.so in the same path as the C++ file (ex1.so can also be in a path defined byLD_LIBRARY_PATH). On Linux, you use the different API functions to dynamically load the function. The functions I use here are defined in dlfcn.h file. In this program,dlopen() opens ex1.so.dlsym() retrieves function's address, anddlclose() unloads the library. When ex1lnx.cpp (Listing 3) is compiled, it should be linked with DLL; for example:

g++ -o ex1lnx ex1lnx.cpp -ldl

        Otherwise, there will be undefined references errors.

        The second example is about complex numbers. Listing 4 shows the F77 function, which takes one COMPLEX input,x, and returns aCOMPLEX result of2*x. The DLL and shared library are ex2.dll on Windows and ex2.so on Linux.Listing 5 (ex2win.cpp) is the C++ file of Listing 2 on Windows, and Listing 6 (ex2lnx.cpp) is the C++ file on Linux. You can see that in C++, the complex class can be used almost as easily as other data types such asdouble orfloat. Just like the first example, the C++ complex variable should also be passed by reference into the F77 function.

        Listing 7 is the F77 subroutine, which calculates the dot product of two arrays, and saves the result in another array. The shared libraries are ex3.dll on Windows and ex3.so on Linux.Listing 8 (ex3win.cpp) is the C++ file on Windows, and Listing 9 (ex3lnx.cpp) is the C++ file on Linux. In this case, C++ pointers are passed for F77 arrays.

        The fourth example deals with function pointers that are useful in scientific computations. The F77 function inListing 10 has two arguments: one is an external function name, the other a REAL type. The function evaluates the external function at the given argument and returns the result.

        The shared libraries are ex4.dll on Windows and ex4.so on Linux. Listings 11 (ex4win.cpp) and 12 (ex4lnx.cpp) are the C++ files on Windows and Linux, respectively. In this case, a C++ function pointer can be directly passed for an F77 function name. The C++ function to be evaluated in the F77 function should be defined in such a way that it takes references of data.

        The last example is about Muller's method and demonstrates this calling method. This method can be used to find any number of roots (real and/or complex) of an arbitrary function. The source code was written in F77 and can be found athttp://www.netlib.org/. The definition of the functionmuller() is:

subroutine muller (f,eps,nsig,kn,nguess,n,x,itmax,infer,ier)
implicit double precision (a-h,o-z)
dimension x(1),infer(1)

        In the argument list, f is an external function whose roots will be saved inx.

        The shared libraries are muller.dll on Windows and muller.so on Linux.Listings 13 (ex5win.cpp) and 14 (ex5lnx.cpp) show the C++ files on Windows and Linux, respectively. In C++ files, the same functionmyFunc() is defined, which is simply a polynomial inx, made by the tenth power of(x-1). Therefore, the actual roots of this functions are ten 1s. Don't be surprised if the results differ from the analytical ones — it is a tough numerical problem to find roots of such a polynomial. In fact, Muller's method does a good job. To call themuller_() function in the shared libraries, a function pointer should be defined in the C++ files, just like the previous examples. In C++ files, pointers can be passed to F77 arrays, and single variables must be passed by references.

Conclusion

        The technique I've presented here for calling Fortran functions from C++ via shared libraries is not completely portable on different operating systems. The major advantage of this method is that the source codes of Fortran and C++ can be separated. As demonstrated in the examples, you can use the native data types in C++ when dynamically calling the F77 functions, giving you much more flexibility.

源码直接下载地址: https://pan.quark.cn/s/95437fdf229e Intel I-219V网卡驱动是一款专门为Intel的I-219V千兆以太网控制器而研发的驱动程序,其主要作用在于保障在Ubuntu 16.04操作系统环境下的正常运作以及优化系统性能。Intel I-219V作为一款广泛应用的内置网络接口控制器(NIC),常被集成在台式机及笔记本电脑的主板上,负责提供高速的网络连接服务。Intel公司所提供的e1000e驱动是与此硬件相配套的开源驱动解决方案,其中版本3.3.5.3是专门针对该硬件设备的定制版本。此驱动包含了不可或缺的源代码部分,赋予开发者和系统管理者按照特定需求进行编译和定制的权限,从而能够适应多样化的系统配置或针对特定情形进行问题解决。源代码的可用性同样表明用户有能力依据Linux内核的更新情况来升级驱动,确保与最新技术标准的兼容性。在Ubuntu 16.04系统中成功编译的驱动意味着它已经通过了严苛的测试流程,并能够与该版本的Linux内核实现良好兼容。Ubuntu 16.04,其代号为Xenial Xerus,是一个长期支持(LTS)的版本,因此对于那些追求系统稳定性和安全保障的用户群体而言具有特殊的意义。驱动程序的兼容性保障了I-219V网卡能够在该系统平台上实现无缝运行,提供稳定可靠的网络连接,这既包括局域网(LAN)的连接,也可能涵盖通过Wi-Fi桥接实现的无线网络连接。驱动程序的核心职责涵盖了网络接口的初始化与管理、数据包的接收与发送处理,以及错误检测与纠正功能的执行。在Linux操作系统架构中,驱动通常以模块的形式加载至内核之中,这种设计允许在非必要时期进行卸载操作,以此来有效节省系统资源。e1000e驱...
内容概要:本文围绕基于共识的捆绑算法(CBBA)在多智能体系统中的多任务分配问题展开研究,重点应用于远程太空船交会与维修的相对轨道操作(RPO)规划。通过Matlab代码实现了CBBA算法,系统地解决了多个航天器在复杂空间环境下协同执行多目标任务时的任务分配、路径规划与动态协商问题。研究详细展示了算法在任务分解、竞标机制、共识达成及冲突消解等方面的核心逻辑,验证了其在分布式决策、通信受限条件下的高效性与鲁棒性,并结合航天工程实际背景突出了算法的应用价值。该资源不仅提供完整的仿真代码,还包含详细的流程解析,有助于深入理解多智能体协同机制的设计原理。; 适合人群:具备控制理论、航天器动力学、多智能体系统或分布式优化背景的研究生、科研人员及航空航天领域工程技术人员,熟练掌握Matlab编程者尤佳。; 使用场景及目标:①应用于在轨服务、空间碎片清除、多航天器编队飞行、星座维护等多智能体协同任务的任务分配与规划;②为研究人员提供CBBA算法的实现范例,支撑其开展分布式任务规划算法的改进与扩展研究;③作为教学案例用于高级课程中讲解多智能体协同决策机制。; 阅读建议:建议结合Matlab代码逐模块分析算法实现过程,重点关注任务打包、竞标更新、共识收敛等关键环节,可尝试引入通信延迟、故障容错或障碍规避机制以进一步提升算法实用性。
内容概要:本文介绍了一种基于关键场景辨别算法的两阶段鲁棒微网优化调度方法,旨在有效应对风电等可再生能源出力不确定性带来的调度挑战。通过Matlab代码实现,构建了包含预调度与实时调整的两阶段鲁棒优化模型,第一阶段制定初始调度计划以应对不确定性,第二阶段根据实际运行数据进行修正,从而提升微网运行的经济性与可靠性。该方法结合场景生成与缩减技术,识别关键不确定性场景,降低计算复杂度,同时增强了调度方案的鲁棒性。文中还探讨了该方法与智能优化算法、机器学习及电力系统仿真工具的集成应用,展现了其在复杂综合能源系统中的广阔应用前景。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事新能源、微网优化、不确定性建模与鲁棒调度等领域研究的科研人员、工程技术人员及研究生。; 使用场景及目标:①应用于高比例可再生能源接入的微电网优化调度,提高系统对源荷不确定性的适应能力与运行稳定性;②为科研人员提供可复现的两阶段鲁棒优化建模与求解范例,支撑高水平学术论文的复现、算法改进与创新研究。; 阅读建议:建议结合提供的Matlab代码与网盘资料,动手实践关键场景生成、不确定性建模、两阶段优化建模与求解全过程,重点关注鲁棒优化框架的设计逻辑与关键场景辨别的实现机制,同时参考文中提及的多种算法与工具,拓展研究思路与应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值