(编辑)TL; DR :我的问题是我虽然Win32 API定义的是真正的整数常量(如在平台SDK标题中),而Win32 Perl包装器将它们定义为 subs 。因此导致单线解析被误解。
在单行中测试Win32::MsgBox
时,我对以下内容感到困惑:给出MsgBox
可能的参数是消息,一个标志的总和来选择那种按钮(值0..5)和消息框图标“常量”(MB_ICONSTOP
,...)和标题
调用perl -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQUESTION, hello"
会得到预期的结果
虽然看起来类似的代码perl -MWin32 -e"Win32::MsgBox world, MB_ICONQUESTION+4, hello"
是错误的
我首先认为它来自我缺少括号,但添加一些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()
的列表中,args T+1, 1+T
被解析为T(1, 43)
...
答案 0 :(得分: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.
其中在我看来,只有第一个是透明的,而其他的有点模糊。
答案 1 :(得分:5)
这与你如何调用T
以及perl如何解释结果有关。
如果我们解析你的例子,我们得到:
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" );
产生相同的结果。
这是因为在没有括号的情况下调用子程序时的优先顺序 - 你可以这样做:
print "one", "two";
两者都被视为print
的参数。 Perl 假设将sub
传递给它之后的参数。
+4
被枚举为参数,并传递给T
。
sub test { print @_,"\n";};
test 1;
test +1;
如果我们解析这个,我们看到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;
或者如上所述 - 代码中的变通方法 - 不要使用+
而是使用|
,它会对位标记操作产生相同的结果,但是由于运算符优先级永远不会被传递到子程序。 (或者当然,总是指定常量的括号)