c++和python調用fortran77生成dll同理,但需要注意subroutine名稱要大寫,不然不認
Fortran(90)代碼如下:
! fortranDLLExample.f90
!
! FUNCTIONS/SUBROUTINES exported from fortranDLLExample.dll:
! fortranDLLExample - subroutine
FUNCTION ABZERO(P) bind(C,name="ABZERO")
!DEC$ ATTRIBUTES DLLEXPORT :: ABZERO
!DEC$ ATTRIBUTES VALUE :: P
integer, INTENT(IN) :: P
integer :: ABZERO
integer :: result1
! Examle calculation
result1 = P - 273
print *, "The p==", P, ";"
print *, "The p-273 [", result1, "]"
ABZERO = result1
RETURN
END FUNCTION
FUNCTION ABZERO_FLOAT(P) bind(C,name="ABZERO_FLOAT")
!DEC$ ATTRIBUTES DLLEXPORT :: ABZERO_FLOAT
!DEC$ ATTRIBUTES VALUE :: P
real, INTENT(IN) :: P
real :: ABZERO_FLOAT
real :: result1
! Examle calculation
result1 = P - 273.15
print *, "The p==", P, ";"
print *, "The p-273 [", result1, "]"
ABZERO_FLOAT = P - 273.15
RETURN
END FUNCTION
FUNCTION sum(length1, P) bind(C,name="sum")
!DEC$ ATTRIBUTES DLLEXPORT :: sum
implicit none
integer::length1
real, dimension(*):: P
real :: sum
integer::i
print *, "The length1==", length1, ";"
sum =0.0
do i = 1,length1 ! sum the array elements
sum = sum + p(i)
PRINT *,p(i)
end do
RETURN
END FUNCTION
subroutine func01( a ) bind(C,name="func01")
!DEC$ ATTRIBUTES DLLEXPORT :: func01
implicit none
character(len=1), dimension(90) , intent(in) :: a
character(len=30), dimension(3) :: b
integer*4 :: count,i,j
count=1
do j=1,3
b(J)=''
do I=1,30
b(J)=trim(b(J))//a(count)
count=count+1
enddo
enddo
print *
print *, "char length = ", len(b(1)), len(b(2)), len(b(3))
print *, "raw a(1) : [", b(1), "]"
print *, "raw a(2) : [", b(2), "]"
print *, "raw a(3) : [", b(3), "]"
print *, "trim : [", trim(b(1)), "] [", trim(b(2)), "] [", trim(b(3)), "]"
end
FUNCTION SUMANDTIMES(P, length1, Q) bind(C,name="SUMANDTIMES")
!DEC$ ATTRIBUTES DLLEXPORT :: SUMANDTIMES
implicit none
integer::length1
real, dimension(*):: P
real :: SUMANDTIMES, Q
integer::i
print *, "The length1==", length1, ";"
SUMANDTIMES =0.0
do i = 1,length1 ! sum the array elements
SUMANDTIMES = SUMANDTIMES + p(i) * Q
PRINT *,p(i)
end do
RETURN
END FUNCTION
FUNCTION array2by2(P, row, col) bind(C,name="array2by2")
!DEC$ ATTRIBUTES DLLEXPORT :: array2by2
USE ISO_C_BINDING
implicit none
integer::row, col, i, j, array2by2
!real,DIMENSION(row*col)::
real P(*)
real PP(row, col)
print *, "---->row-----", row
print *, "---->col-----", col
do i=1,row
do j=1,col
PP(i,j) = P((i-1)*row+j)
P((i-1)*row+j) = PP(i,j) * 100
print *, PP(i,j), i, j, (i-1)*row+j
end do
print *, "----row-----", i
end do
array2by2 = 0
RETURN
END FUNCTION
Python調用代碼如下(dll放在py檔案的同一目錄)
# coding=utf-8
from _ctypes import byref
from ctypes import cdll, c_int, c_float
import ctypes
my_dll = cdll.LoadLibrary('fortranDLLExample.dll')
result = my_dll.ABZERO(c_int(324))
print "my_dll.ABZERO(c_int(324))==", result, type(result)
my_dll.ABZERO_FLOAT.restype = c_float # 要設定傳回值的類型,不然預設會傳回int型,導緻資料混亂
result = my_dll.ABZERO_FLOAT(c_float(500.44))
print "my_dll.ABZERO_FLOAT(c_float(500.44))", result, type(result)
# tip:dll.addf.argtypes = (c_float, c_float)
# addf 有兩個形參,都是 float 類型。雖然也可以用數組設定,但是元祖的效率更高
FloatArray = c_float * 5
ia = FloatArray(5.32, 1.12, 7.321, 33.12, 99.3)
for i in ia:
print i
my_dll.sum.restype = c_float
result = my_dll.sum(byref(c_int(len(ia))), byref(ia))
print "dll.sum == ", result
FloatArray = c_float * 3
ia2 = FloatArray(1.1, 2.2, 3.3)
my_dll.SUMANDTIMES.restype = c_float
result = my_dll.SUMANDTIMES(byref(ia2),
byref(c_int(len(ia2))),
byref(c_float(100.1)))
print "dll.SUMANDTIMES == ", type(result), result
# 出現win32不是有效的應用程式bug的時候,記得在fortran編輯器選擇好x86或x64,版本要和python的位數對應上
d = ctypes.create_string_buffer("abcde67890b234567 "
"waeofijo "
"awoijewfawfe ", size=90)
my_dll.func01(d)
j_table = (c_float * 16)(1.1, 1.2, 1.3, 1.4,
2.1, 2.2, 2.3, 2.4,
3.1, 3.2, 3.3, 3.4,
4.1, 4.2, 4.3, 4.4) # 給力
my_dll.array2by2(byref(j_table),
byref(c_int(4)),
byref(c_int(4)))
for i in j_table:
print i,
print "--------------------- 操作完後的結果 ---------------------"
h_table = (c_float * 16)(91.1, 51.2, 1.35, 1.45,
92.1, 52.2, 2.35, 2.45,
93.1, 53.2, 3.35, 3.45,
94.1, 54.2, 4.35, 4.45) # 給力
my_dll.array2by2(byref(h_table),
byref(c_int(4)),
byref(c_int(4)))
for i in h_table:
print i,
print "--------------------- 操作完後的結果 ---------------------"
my_dll.array2by2(byref(j_table),
byref(c_int(4)),
byref(c_int(4)))
for i in j_table:
print i,
print "預設傳給dll的是指針,是以在fortran中如果動了位址裡面的内容,那麼反映到python上也會跟着改變"
輸出結果如下:
The p== 324 ;
The p-273 [ 51 ]
my_dll.ABZERO(c_int(324))== 51
The p== 500.4400 ;
The p-273 [ 227.2900 ]
my_dll.ABZERO_FLOAT(c_float(500.44)) 227.290008545
5.32000017166
1.12000000477
7.32100009918
33.1199989319
99.3000030518
The length1== 5 ;
5.320000
1.120000
7.321000
33.12000
99.30000
dll.sum == 146.180999756
The length1== 3 ;
1.100000
2.200000
3.300000
dll.SUMANDTIMES == 660.66003418
char length = 30 30 30
raw a(1) : [abcde67890b234567 ]
raw a(2) : [waeofijo ]
raw a(3) : [awoijewfawfe ]
trim : [abcde67890b234567] [waeofijo] [awoijewfawfe]
---->row----- 4
---->col----- 4
1.100000 1 1 1
1.200000 1 2 2
1.300000 1 3 3
1.400000 1 4 4
----row----- 1
2.100000 2 1 5
2.200000 2 2 6
2.300000 2 3 7
2.400000 2 4 8
----row----- 2
3.100000 3 1 9
3.200000 3 2 10
3.300000 3 3 11
3.400000 3 4 12
----row----- 3
4.100000 4 1 13
4.200000 4 2 14
4.300000 4 3 15
4.400000 4 4 16
----row----- 4
110.0 120.000007629 130.0 140.0 209.999984741 220.0 230.0 240.000015259 310.0 320.0 330.0 340.0 410.0 419.999969482 430.000030518 440.0 --------------------- 操作完後的結果 ---------------------
---->row----- 4
---->col----- 4
91.10000 1 1 1
51.20000 1 2 2
1.350000 1 3 3
1.450000 1 4 4
----row----- 1
92.10000 2 1 5
52.20000 2 2 6
2.350000 2 3 7
2.450000 2 4 8
----row----- 2
93.10000 3 1 9
53.20000 3 2 10
3.350000 3 3 11
3.450000 3 4 12
----row----- 3
94.10000 4 1 13
54.20000 4 2 14
4.350000 4 3 15
4.450000 4 4 16
----row----- 4
9110.0 5120.0 135.0 145.0 9210.0 5220.0 234.999984741 245.0 9310.0 5320.0 335.0 345.0 9410.0 5420.0 435.0 444.999969482 --------------------- 操作完後的結果 ---------------------
---->row----- 4
---->col----- 4
110.0000 1 1 1
120.0000 1 2 2
130.0000 1 3 3
140.0000 1 4 4
----row----- 1
210.0000 2 1 5
220.0000 2 2 6
230.0000 2 3 7
240.0000 2 4 8
----row----- 2
310.0000 3 1 9
320.0000 3 2 10
330.0000 3 3 11
340.0000 3 4 12
----row----- 3
410.0000 4 1 13
420.0000 4 2 14
430.0000 4 3 15
440.0000 4 4 16
----row----- 4
11000.0 12000.0009766 13000.0 14000.0 20999.9980469 22000.0 23000.0 24000.0019531 31000.0 32000.0 33000.0 34000.0 41000.0 41999.9960938 43000.0039062 44000.0 預設傳給dll的是指針,是以在fortran中如果動了位址裡面的内容,那麼反映到python上也會跟着改變
bind(C,name="RUN_S_ENTERFUNC")不是必須的,加上bind後傳入字元串有可能報錯
當使用不定長度數組或者字元串的時候,應該去掉!DEC$ ATTRIBUTES VALUE :: P以避免編譯器反複提示你要給數組配置設定一個固定的長度
The character length of a dummy argument with the VALUE attribute must be an initialization expression. [INPUTFILE]
關于fortran數組:
Syntactically, assumed shape used colons (:) instead of
an asterisk in the dimension declaration. The difference in meaning
is exactly the subject in question - assumed shape arrays automatically
get their shape information passed, whereas assumed size arrays basically
get nothing but a starting address passed.
運作python遇到這個錯誤 error: Unable to find vcvarsall.bat 可以這麼解決:
報錯原因:在生成的時候,編譯器從%PythonInstallPath%\distutils\msvc9compiler.py裡的219行find_vcvarsall(version)函數中找不到vcvarsall.bat檔案。
更具體的原因是,msvc9compiler.py從sys.version裡提取MSVC的版本号,但是在系統資料庫中并沒有根據版本号找到vcvarsall.bat,在系統的環境變量中也沒有找到版本号對應的路徑。後來我根據版本号,在環境變量中添加了路徑,但因為msvc9compiler.py主要是針對VS2008和VS2010所做的路徑識别,是以還是不能正确地找到vcvarsall.bat。
解決方法:直接在find_vcvarsall(version)函數中傳回vcvarsall.bat的絕對路徑。
Tip:運作.bat後,相當于添加了vs各種工具的環境變量,我們可以使用dumpbin /symbols xxx.lib來檢視lib中有多少function供我們使用
setup detected an issue during the operation.
“安裝程式檢測到一個問題......”
解決方法是:
找到C:\Windows\Fonts目錄,
備份fonts下面所有的字型
然後删除C:\Windows\Fonts下面所有的字型(系統字型如果删除不了的,那麼就留着,一些字型集提示警告不要删除的,也留着)
然後再安裝vs2015就OK了!
這個确實問題很詭異,但就是這麼解決的:https://stackoverflow.com/questions/33622151/setup-detected-an-issue-during-visual-studio-community-edition-2015-installation
當出現這個錯誤的時候Visual Studio cannot debug because a debug target has not been specified或者cannot start debugging because the debug target is missing
(在編譯dll的時候尤其容易出現,可以選擇一個exe來debug)
編輯VS裡的fortran時,如果我們希望開啟轉跳到定義功能,在這裡設定:
新bug解決方案:
有人發覺使用python3的時候調用錯誤,debug半天發覺進到dll裡的字元串不對。查找原因,最終發覺是python2、3字元串表述問題。python2字元串預設是ascii的str,而python3是unicode,而一般c語言、fortran語言接收的是char或char數組,是以應該轉為ascii,使用語句:
import ctypes
DLL_PATH = 'C:./Test2.dll'
lib = ctypes.cdll.LoadLibrary(DLL_PATH)
lib.printString('abc'.encode('ascii'))