(编辑) TL;DR:我的问题是,尽管Win32 API定义的是真正的整数常量(就像platform中那样),而Win32 Win32包装器将它们定义为子类。这就导致了一行行解析的误解。
在一个一行程序中测试对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)
。
发布于 2015-05-13 09:51:16
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
,表示正在设置的位)的函数。同样,正如苏布里克所指出的,这是一个标志变量,所以不应该使用加法,而是按位逻辑和/或运算符。
在您的情况下,它只接受任何参数并忽略它们。如果你期望有一个常数,这就有点让人困惑了。
在你的实验案例中,陈述
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
因为+
比逗号,
具有更高的优先,而一元+
甚至更高。
要消除歧义,您需要使用括号来澄清优先级:
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.
在我看来,其中只有第一个是透明的,而另一个则有点模糊。
发布于 2015-05-13 09:53:37
这与您如何调用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中发现了一个bug,可以通过以下方法解决:
sub MB_ICONQUESTION() {0x00000020}
Win32::MsgBox "world", 4 + MB_ICONQUESTION, "hello";
Win32::MsgBox "world", MB_ICONQUESTION + 4, "hello";
或者也许:
use constant MB_ICONQUESTION => 0x00000020;
或者如前所述--代码中的变通方法--不要使用+
,而是使用|
,这对于位标志操作将有相同的结果,但是由于运算符的优先级永远不会传递到子例程中。(当然,总是为常量指定括号)
https://stackoverflow.com/questions/30211089
复制相似问题