回调中的类型转换 您所在的位置:网站首页 fortran整型转换成字符型 回调中的类型转换

回调中的类型转换

2024-06-19 19:30| 来源: 网络整理| 查看: 265

回调中的类型转换¶

类型转换基本上有五种不同的方法,每种都有自己的优缺点。

方法I、II和V可用于C和Fortran。方法III和方法IV只能在Fortran中使用。方法VI已过时,不应使用。

工作数组¶

传递一个“工作数组”,该数组打包调用所需的所有内容,并由被调用的例程解包。这是一种古老的方式–例如,LAPACK正是使用这种方法。

积分器:

module integrals use types, only: dp implicit none private public simpson contains real(dp) function simpson(f, a, b, data) result(s) real(dp), intent(in) :: a, b interface real(dp) function func(x, data) use types, only: dp implicit none real(dp), intent(in) :: x real(dp), intent(inout) :: data(:) end function end interface procedure(func) :: f real(dp), intent(inout) :: data(:) s = (b-a) / 6 * (f(a, data) + 4*f((a+b)/2, data) + f(b, data)) end function end module

用法:

module test use types, only: dp use integrals, only: simpson implicit none private public foo contains real(dp) function f(x, data) result(y) real(dp), intent(in) :: x real(dp), intent(inout) :: data(:) real(dp) :: a, k a = data(1) k = data(2) y = a*sin(k*x) end function subroutine foo(a, k) real(dp) :: a, k real(dp) :: data(2) data(1) = a data(2) = k print *, simpson(f, 0._dp, pi, data) print *, simpson(f, 0._dp, 2*pi, data) end subroutine end module 通用结构¶

定义一个通用的结构,包含目前实际的需求(甚至在未来的需求)。 这个通用结构类型可以根据未来的需求或设想而改变,但不太可能需要比如从传递实数更改为文本编辑器的实例化。

积分器:

module integrals use types, only: dp implicit none private public simpson, context type context ! This would be adjusted according to the problem to be solved. ! For example: real(dp) :: a, b, c, d integer :: i, j, k, l real(dp), pointer :: x(:), y(:) integer, pointer :: z(:) end type contains real(dp) function simpson(f, a, b, data) result(s) real(dp), intent(in) :: a, b interface real(dp) function func(x, data) use types, only: dp implicit none real(dp), intent(in) :: x type(context), intent(inout) :: data end function end interface procedure(func) :: f type(context), intent(inout) :: data s = (b-a) / 6 * (f(a, data) + 4*f((a+b)/2, data) + f(b, data)) end function end module

用法:

module test use types, only: dp use integrals, only: simpson, context implicit none private public foo contains real(dp) function f(x, data) result(y) real(dp), intent(in) :: x type(context), intent(inout) :: data real(dp) :: a, k a = data%a k = data%b y = a*sin(k*x) end function subroutine foo(a, k) real(dp) :: a, k type(context) :: data data%a = a data%b = k print *, simpson(f, 0._dp, pi, data) print *, simpson(f, 0._dp, 2*pi, data) end subroutine end module

真正需要的灵活性只有这么多。例如,可以为此定义两种结构类型,一种用于情况A,另一种用于情况B。然后,每一个都将具有足够的通用性,并包含所有需要的部分和正确的标志。

注意:它不必是“一个包罗万象的抽象类”或空类。在“全部”和“无”之间有自然和可行的选择。

私有的模块变量¶

通过传入模块变量来完全隐藏变量参数。

积分器:

module integrals use types, only: dp implicit none private public simpson contains real(dp) function simpson(f, a, b) result(s) real(dp), intent(in) :: a, b interface real(dp) function func(x) use types, only: dp implicit none real(dp), intent(in) :: x end function end interface procedure(func) :: f s = (b-a) / 6 * (f(a) + 4*f((a+b)/2) + f(b)) end function end module

用法:

module test use types, only: dp use integrals, only: simpson implicit none private public foo real(dp) :: global_a, global_k contains real(dp) function f(x) result(y) real(dp), intent(in) :: x y = global_a*sin(global_k*x) end function subroutine foo(a, k) real(dp) :: a, k global_a = a global_k = k print *, simpson(f, 0._dp, pi) print *, simpson(f, 0._dp, 2*pi) end subroutine end module

但是,如果可能的话,最好避免这样的全局变量–即使实际上只是半全局变量(semi-global)。虽然有时这可能是最简单最干净的方式,然而,只要沿着II或IV的路线稍加思考,通常会有一个更好、更安全、更明确的方案。

嵌套函数¶

积分器:

module integrals use types, only: dp implicit none private public simpson contains real(dp) function simpson(f, a, b) result(s) real(dp), intent(in) :: a, b interface real(dp) function func(x) use types, only: dp implicit none real(dp), intent(in) :: x end function end interface procedure(func) :: f s = (b-a) / 6 * (f(a) + 4*f((a+b)/2) + f(b)) end function end module

用法:

subroutine foo(a, k) use integrals, only: simpson real(dp) :: a, k print *, simpson(f, 0._dp, pi) print *, simpson(f, 0._dp, 2*pi) contains real(dp) function f(x) result(y) real(dp), intent(in) :: x y = a*sin(k*x) end function f end subroutine foo 使用 type(c_ptr) 指针¶

在C语言中,可以使用void*指针。在Fortran中,可以使用type(c_ptr)来达到完全相同的目的。

积分器:

module integrals use types, only: dp use iso_c_binding, only: c_ptr implicit none private public simpson contains real(dp) function simpson(f, a, b, data) result(s) real(dp), intent(in) :: a, b interface real(dp) function func(x, data) use types, only: dp implicit none real(dp), intent(in) :: x type(c_ptr), intent(in) :: data end function end interface procedure(func) :: f type(c_ptr), intent(in) :: data s = (b-a) / 6 * (f(a, data) + 4*f((a+b)/2, data) + f(b, data)) end function end module

用法:

module test use types, only: dp use integrals, only: simpson use iso_c_binding, only: c_ptr, c_loc, c_f_pointer implicit none private public foo type f_data ! Only contains data that we need for our particular callback. real(dp) :: a, k end type contains real(dp) function f(x, data) result(y) real(dp), intent(in) :: x type(c_ptr), intent(in) :: data type(f_data), pointer :: d call c_f_pointer(data, d) y = d%a * sin(d%k * x) end function subroutine foo(a, k) real(dp) :: a, k type(f_data), target :: data data%a = a data%k = k print *, simpson(f, 0._dp, pi, c_loc(data)) print *, simpson(f, 0._dp, 2*pi, c_loc(data)) end subroutine end module

与以往一样,Fortran 允许你在真正需要的情况下使用这种重新转换的优点, 但同时也有一些缺点,即编译和运行时检查可能无法捕获错误。因此,这将不可避免地会有更多的漏洞百出、容易出现 bug 的代码,所以需要考虑平衡成本和收益。

通常,在科学计算中,主要目的是表示和求解精确的数学公式(而不是创建一个包含无数按钮、下拉框和其它界面元素的GUI),所以,最简单、最不容易出错、最快的选择是使用前面的方法之一。

transfer()内置函数¶

在Fortran 2003之前,做类型转换的唯一方法是使用transfer内置函数。它在功能上与方法V等效,但更冗长,更容易出错。它现在已经过时了,应该使用方法V来代替。

例子:

http://jblevins.org/log/transfer

http://jblevins.org/research/generic-list.pdf

http://www.macresearch.org/advanced_fortran_90_callbacks_with_the_transfer_function

面向对象的策略¶

如下模块:

module integrals use types, only: dp implicit none private public :: integrand, simpson ! User extends this type type, abstract :: integrand contains procedure(func), deferred :: eval end type abstract interface function func(this, x) result(fx) import :: integrand, dp class(integrand) :: this real(dp), intent(in) :: x real(dp) :: fx end function end interface contains real(dp) function simpson(f, a, b) result(s) class(integrand) :: f real(dp), intent(in) :: a, b s = ((b-a)/6) * (f%eval(a) + 4*f%eval((a+b)/2) + f%eval(b)) end function end module

抽象类型准确地规定了集成例程需要什么,即函数求值的方法,但不会对用户强加任何其它东西。 用户扩展此类型,提供 eval 类型绑定过程的具体实现,并添加必要的上下文数据作为扩展类型的成员。

用法:

module example_usage use types, only: dp use integrals, only: integrand, simpson implicit none private public :: foo type, extends(integrand) :: my_integrand real(dp) :: a, k contains procedure :: eval => f end type contains function f(this, x) result(fx) class(my_integrand) :: this real(dp), intent(in) :: x real(dp) :: fx fx = this%a*sin(this%k*x) end function subroutine foo(a, k) real(dp) :: a, k type(my_integrand) :: my_f my_f%a = a my_f%k = k print *, simpson(my_f, 0.0_dp, 1.0_dp) print *, simpson(my_f, 0.0_dp, 2.0_dp) end subroutine end module void * vs type(c_ptr) 和 transfer() 的完整示例¶

这里有三个等价代码:在C中使用void*,在Fortran中使用type(c_ptr)和transfer():

语言  

方法

链接

C

void *

https://gist.github.com/1665641

Fortran

type(c_ptr)  

https://gist.github.com/1665626

Fortran

transfer()

https://gist.github.com/1665630

C代码使用标准的C方法来编写接受回调和上下文的可扩展库。两个Fortran代码展示了如何做同样的事情。type(c_ptr)方法与C版本等效,这是应该使用的方法。

这里的transfer()方法只是为了完善对比(在Fortran2003之前,这是唯一的方法),它有点麻烦,因为用户需要为每个类型创建辅助转换函数。因此,推荐使用type(c_ptr)方法代替它。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有