2015-05-13 32 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