笨笨学的第一门编程语言,就是Fortran,当时用的是谭浩强老师编写的教材。不像计算机专业的人士,貌似他们一般要先学Pascal。
岁月轮回春多少,得失淡淡笑一笑,笑问人间谁是客,无来无去莫计较。
现在,笨笨用C++比较多,Fortran基本上都还给老师了!但是,感觉Fortran还是挺有用的。
Fortran源自于“公式翻译”(英语:FormulaTranslation)的缩写,是一种编程语言。它是世界上第一种高级程序设计语言,广泛应用于科学和工程计算领域。FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。
Fortran的特长在于数值计算,计算效率高,另外矩阵和复数都是其内置的数据类型,极其方便。
但是,正是由于长于数值计算,Fortran在界面方面不是特别方便,当然现在的Fortran也可以调用系统的API进行界面编程,还有一些第三方的界面库,例如Winteractor,但总的说来,界面编程远不如C++灵活。
Fortran下面绘制数据曲线,当然也有不少工具,比如Winteractor,里面就有曲线绘制工具,又比如Dislin,但这俩都是商业软件,Compaq Visual Fortran安装文件的第二张光盘,是Array Visualizer,这也是一个数据可视化工具。
CChart比起这些工具,就显得轻量化多了,同时功能也是很全面的。
下面笨笨来介绍一下CChart在Fortran控制台程序下的应用。
CChart是用C++编制的,因此这里就存在一个混合编程的问题。笨笨先介绍从CChart的Dll文件导出绘图函数给Fortran调用的方法。如果后续有一定的反响,可以考虑把所有函数都提前导出,这样使用就方便了。不过,本节课还是以介绍方法为主。
本节课采用的Fortran IDE是Compaq Visual Fortran 6.6,这是一个比较古老的版本,目前版本是Intel Visual Fortran。由于是继承关系,本节课的方法也可以在新版本上应用。(说起来,Visual Fortran真是命运多舛,其亲生父亲是Microsoft,最终版本是Fortran Power Station 4.0,随后,被卖给Dec,变成Digital Visual Fortran,此后顺序是Compaq Visual Fortran,HP Visual Fortran,Intel Visual Fortran)
在CChart上免不了要操作字符串,而字符串就要分多字节和宽字节版本,由于笨笨还不知道怎么在Fortran里面使用宽字节字符串,所以目前只能使用多字节字符串,这和前面C#、Delphi、C++Builder、易语言等环境里面不一样。具体说来,就是我们要用到的多字节库文件是CChart.lib和CChart.dll,而不是宽字节库文件CChartu.lib和CChartu.dll。这样,我们在导出C++函数的时候,函数的内部名称需要到CChart_EXPORTS.txt这个文件里面去查询。
下面首先给出一个在Fortran控制台绘制数据曲线的例子。
第一步,打开Visual Studio,建立一个Fortran Console Application,名为LessonA34。
第二步,选择 A “Hello World” Sample Project,并结束。
第三步,把我们需要的CChart.lib和CChart.dll两个文件拷贝到LessonA34文件夹里面。
第四步,把CChart.lib添加到项目里。
第五步,在LessonA34.f90里面,首先导入需要用到的几个函数。
interface
integer function CreatePopupChartWndGlobal(hwnd, charttype, title, x, y, cx, cy)
!DEC$ATTRIBUTES DLLIMPORT,ALIAS:'?CreatePopupChartWndGlobal@Classless@@YAPAUHWND__@@PAU2@HPADHHHH@Z'::CreatePopupChartWndGlobal
!DEC$ATTRIBUTES VALUE::hwnd, charttype, title, x, y, cx, cy
integer hwnd, charttype, title, x, y, cx, cy
end function CreatePopupChartWndGlobal
integer function AddPoint2D(x, y, dataIndex, plotIndex)
!DEC$ATTRIBUTES DLLIMPORT,ALIAS:'?AddPoint2D@Classless@@YAHNNHH@Z'::AddPoint2D
!DEC$ATTRIBUTES VALUE::x, y, dataIndex, plotIndex
double precision x, y
integer dataIndex, plotIndex
end function AddPoint2D
subroutine SetTitle(title)
!DEC$ATTRIBUTES DLLIMPORT,ALIAS:'?SetTitle@Classless@@YAXPBD@Z'::SetTitle
!DEC$ATTRIBUTES VALUE::title
integer title
end subroutine SetTitle
subroutine ReDraw()
!DEC$ATTRIBUTES DLLIMPORT,ALIAS:'?ReDraw@Classless@@YAXXZ'::ReDraw
end subroutine ReDraw
integer function SimpleMsgLoop()
!DEC$ATTRIBUTES DLLIMPORT,ALIAS:'?SimpleMsgLoop@NsCChart@@YAHXZ'::SimpleMsgLoop
end function SimpleMsgLoop
end interface
上面这么多代码,实际上的作用是从CChart.dll文件里导入了5个后面要用到的函数,CreatePopupChartWndGlobal,AddPoint2D,SetTitle,ReDraw,SimpleMsgLoop。
至于为什么要导入这些函数,请参考快乐高四第二十一课C风格代码,以及上一课C++控制台程序。
这里有几个问题要说明一下。
1)本节课的Fortran控制台程序和上节课的C++控制台程序,虽然都是控制台程序,实际上编程模式差别很大。上节课的控制台程序,是利用了CChart类库提供的ChartCtrl这个Windows标准控件,关键之处是GetChart(HWND)这个函数可以返回一个CChart类对象的指针,但本节课的编程语言是Fortran,即使获得了这个指针,也没法使用。
Fortran没法使用CChart的类,只能调用其提供的全局函数,也就是前面已经讲过的C风格代码,只是需要把C风格代码翻译成Fortran语言。
2)CreatePopupChartWndGlobal这个函数看起来和上节课的CreatePopupChartWnd很像,参数也完全相同,但实际上内部实现方式是完全不一样的。
CreatePopupChartWnd是WindowsAPI CreateWindow的一个简单封装,建立一个窗口类名为ChartCtrl的Windows窗口。
本节课的CreatePopupChartWndGlobal则是建立了一个Win32 Button窗口,并利用窗口子类化技术,把快乐高四第二十一课里面提到的pGlobalChartWnd这个全局变量,Attach到这个Button窗口上。由于CChart类库提供的大部分全局函数都是操作pGlobalChartWnd的,所以这些函数都可以直接使用了。具体可以用的函数请打开Classless.h文件查看。
本节课这个实现方式主要是针对Fortran编程设计的。
3)对于CChart的有返回函数,Fortran里面需要导出为function,无返回函数则导出为subroutine。
4)ALIAS后面双引号里面是CChart.dll里面函数的实际名称,比较复杂,具体需要到CChart_EXPORTS.txt里面查询。
5)Fortran的参数默认都是传地址,但CChart是用C++编制的,很大部分参数都是传值的,为了避免二者的矛盾,导入函数时,利用!DEC$ATTRIBUTES VALUE::Parameter来改变Fortran的参数传递方式。
6)C++里面的很多类型看起来很高大上的参数,如HWND等,在Fortran里面都可以用integer来表示。
7)Fortran里面Unicode宽字节的字符串不好处理,这里凡是涉及到字符串的参数,都采用多字节字符串,也就是Fortran里面的Character*。
但在函数的参数里面,对于字符串参数,我们不直接采用Character*,而是采用传值的方式使用integer,这样传入的是字符串首字母的地址。调用的方式后面会有,用LOC(“string”C)的格式,这里LOC可以取地址,而后缀C会在字符串后面添加一个结束符”\0”,因为Fortran字符串没有结束符,而C语言字符串需要结束符。
8)这里主要介绍的函数导出的方法。如果本课程发布后,具有较大的反响,笨笨可以考虑把所有函数都导出到一个模块文件里面,后面大家直接用这个文件就可以了,不需要再这样费时费力地导出函数。但如果用的人少,就没有必要做这项工作了,毕竟笨笨的精力是有限的,需要做一些有意义的事。
下面继续我们的代码。
第六步,在implicit none的下一行,添加一个变量用于循环。
integer i
第七步,在print *, 'Hello World'的下一行,添加下列代码。
print *, "创建弹出窗口..."
call CreatePopupChartWndGlobal(0, 0, LOC("窗口标题"C), 50, 50, 600, 450)
print *, "设置标题"
call SetTitle(LOC("Fortran标题"C))
print *, "添加数据点..."
do i = -10, 10, 1
call AddPoint2D(i, i*i*i, 0, 0);
end do
print *, "重绘..."
call ReDraw()
print *, "建立消息循环..."
call SimpleMsgLoop()
这些代码不用讲解了,因为已经自注释了。
运行程序,得到的图像如下。
注意到是有交互功能的哟!
控制台窗口如图。
本节课对Fortran下CChart的应用进行了比较全面的介绍,后续将针对具体情况,进行较深入的讨论。