2015-05-13 72 views
6

(編輯)TL; DR:我的問題是,我雖然在Win32 API定義是真實的整數常量(如平臺SDK頭),而在Win32 Perl的包裝將它們定義爲潛艇。從而導致了單線解析被誤解。給人對於MsgBox可能的參數是消息,標誌之和來選擇那種按鈕(值0:(4 + sub)不等於(sub + 4)?


雖然在一個班輪測試,以Win32::MsgBox一個電話,我對下面的困惑..5)和消息框圖標 「常數」(MB_ICONSTOP,...)和標題

調用perl -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQUESTION, hello"給出了預期的結果

OK

WH ILE在尋找類似的代碼perl -MWin32 -e"Win32::MsgBox world, MB_ICONQUESTION+4, hello"是錯誤的

NOK

我第一次,雖然它來自我缺乏括號,但增加了一些perl -MWin32 -e"Win32::MsgBox (world, MB_ICONQUESTION+4, hello)"給完全相同的錯誤的結果。

我與同事試圖更深入和顯示傳遞給函數調用(作爲MB_xxx常量實際上潛艇)用下面的代碼

>perl -Mstrict -w -e"sub T{print $/,'called T(#'.join(',',@_).'#)'; 42 }; print $/,'results:', join ' ,', T(1), T+1, 1+T" 

輸出

called T(#1#) 
called T(##) 
called T(#1,43#) 
results:42 ,42 
參數

但我不明白爲什麼在列表傳遞給join()的參數T+1, 1+T被解析爲T(1, 43) ...

+3

對這個問題的看法顯然是錯誤的。這是一個有關混淆子程序和運算符優先級的有效問題。 – TLP

+2

有趣的是,你調用'MB_ICONQUESTION'是一個常量,然後用子例程進行實驗。你的問題的標題應該指出子程序,而不是常量。 – TLP

+2

@TLP:我的和尚同事指出,'MB_ICONQUESTION'實際上是一個子(因爲常量通常是perl中的subs),所以我們嘗試了後面的測試。我將更改標題 – Seki

回答

9

B::Deparse救援:

C:>perl -MO=Deparse -MWin32 -e"Win32::MsgBox world, MB_ICONQUETION+4, hello" 
use Win32; 
Win32::MsgBox('world', MB_ICONQUESTION(4, 'hello')); 
-e syntax OK 

C:>perl -MO=Deparse -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQESTION, hello" 
use Win32; 
Win32::MsgBox('world', 4 + MB_ICONQUESTION(), 'hello'); 
-e syntax OK 

MB_ICONQUESTION呼叫在第一種情況下被認爲是與參數+4, 'hello'一個函數調用。在第二種情況下,它被認爲是一個沒有參數的函數調用,並添加了4個參數。它似乎並不是一個常數,而是一個函數。

在源代碼中,我們得到這個驗證:

sub MB_ICONQUESTION      { 0x00000020 } 

這是返回32(二進制00100000,指示位被設置)的功能。同樣如Sobrique指出的那樣,這是一個標誌變量,所以你不應該使用加法,而是按位邏輯和/或操作符。

在你的情況下,它只接受任何參數並忽略它們。如果你期望一個不變的話,這有點令人困惑。

在實驗的情況下,該語句

print $/,'results:', join ' ,', T(1), T+1, 1+T 

解釋

print $/,'results:', join ' ,', T(1), T(+1, (1+T)) 

因爲從右到左執行去

1+T = 43 
T +1, 43 = 42 
T(1) = 42 

因爲加+比逗號,更高precedence ,和一元+甚至更​​高。

消除歧義,你需要做的使用括號明確優先順序:

print $/,'results:', join ' ,', T(1), T()+1, 1+T 
#          ^^-- parentheses 

作爲一般規則,每個人都應該使用括號子程序調用。在perldoc perlsub有4個調用符號:

NAME(LIST); # & is optional with parentheses. 
NAME LIST;  # Parentheses optional if predeclared/imported. 
&NAME(LIST); # Circumvent prototypes. 
&NAME;   # Makes current @_ visible to called subroutine. 

其中在我看來,只有第一個是透明的,而其他的人有點模糊。

+5

最後一個提供「常量」的模塊中的一個錯誤,它忘記給出存在的'()'原型來解決這個問題。 – LeoNerd

+2

或者只是'使用常數',我認爲它也可以正確處理。 – Sobrique

+0

@LeoNerd原型比解決方案更經常是問題。它們的存在使得可以使子程序像內置程序一樣工作,而不是進行參數檢查。 – TLP

5

這與你如何調用T以及perl如何解釋結果有關。

如果我們deparse您的例子中,我們得到:

BEGIN { $^W = 1; } 
sub T { 
    use strict; 
    print $/, 'called T(#' . join(',', @_) . '#)'; 
    42; 
} 
use strict; 
print $/, 'results:', join(' ,', T(1), T(1, 1 + T())); 

這顯然不是你的想法已經得到了什麼,但並解釋爲什麼你會得到你的結果。

我建議你例子 - 寧可+你不妨考慮使用|,因爲它看起來非常像MB_ICONQUESTION旨在成爲一個標誌。

所以:

use strict; 
use warnings; 

use Win32 qw(MB_ICONQUESTION); 

print MB_ICONQUESTION; 

Win32::MsgBox("world", 4 | MB_ICONQUESTION , "hello"); 

或者

use strict; 
use warnings; 

use Win32 qw(MB_ICONQUESTION); 

print MB_ICONQUESTION; 

Win32::MsgBox("world", MB_ICONQUESTION | 4 , "hello"); 

產生相同的結果。

這是因爲precence的不帶括號調用子程序的時候 - 你可以這樣做:

print "one", "two"; 

而且兩者都被視爲參數print。 Perl 假定sub之後的參數將被傳遞給它。

+4被枚舉爲一個參數,並傳遞給T

sub test { print @_,"\n";}; 

test 1; 
test +1; 

如果我們deparse這一點,我們看到Perl將它作爲:

test 1; 
test 1; 

所以,最後 - 有在Win32中的一個錯誤,你已經找到,那將是可以解決的:

sub MB_ICONQUESTION() {0x00000020} 

Win32::MsgBox "world", 4 + MB_ICONQUESTION, "hello"; 
Win32::MsgBox "world", MB_ICONQUESTION + 4, "hello"; 

或許:

use constant MB_ICONQUESTION => 0x00000020; 

Ø r注意到 - 代碼中的解決方法 - 不使用+,而是使用|,它將對位標誌操作具有相同的結果,但由於運算符優先級永遠不會傳遞到子例程中。 (或者當然,總是爲你的常量指定括號)

+0

感謝提示使用二進制'OR'而不是加法,順便說一句,我沒有考慮使用明確的括號,因爲一開始我不知道API常量實際上不是一個常量,而是一個子...我是有點Perl新手:o) – Seki

+0

每個人在開始時都會啓動一種語言。你在那裏提出了一個很好的問題,這非常值得讚賞 - 有趣並且有足夠的深度來重現和調查。 – Sobrique