2015-05-13 16 views
6

(edit) TL; DR: il mio problema è che ho anche se le definisce API Win32 erano vere interi costanti (come nelle intestazioni di Platform SDK), mentre l'involucro Win32 Perl li definisce subs. Così ha causato l'incomprensione dell'analisi one-liner.(4 + sub) non è uguale a (sub + 4)?


Durante la prova in un one-liner una chiamata a Win32::MsgBox, io sono perplesso dal seguente: dare che i possibili argomenti per MsgBox sono il messaggio, una somma di bandiere di scegliere il tipo di pulsanti (valore 0 ..5) e finestra di messaggio icona "costanti" (MB_ICONSTOP, ...) e il titolo

chiamando perl -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQUESTION, hello" dà il risultato atteso

OK

wh ile il codice simile cercando perl -MWin32 -e"Win32::MsgBox world, MB_ICONQUESTION+4, hello" è sbagliato

NOK

ho però che deriva dalla mia mancanza di parentesi, ma l'aggiunta di qualche perl -MWin32 -e"Win32::MsgBox (world, MB_ICONQUESTION+4, hello)" dà esattamente lo stesso risultato sbagliato.

Ho provato con un collega scavare più a fondo e visualizzare i parametri che vengono passati ad una chiamata di funzione (i MB_xxx costanti sono effettivamente sub) con il seguente codice

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

che emette

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

ma non riesco a capire il motivo per cui nella lista passata a join() args T+1, 1+T vengono verificati come T(1, 43) ...

+3

Il downvote su questa domanda è chiaramente sbagliato. Questa è una domanda valida su subroutine alquanto confusa e sulla precedenza degli operatori. – TLP

+2

È interessante che tu chiami "MB_ICONQUESTION" una costante, ma poi sperimenta le subroutine. Il titolo della tua domanda dovrebbe indicare subroutine, non costanti. – TLP

+2

@TLP: il mio collega monaco mi ha indicato che "MB_ICONQUESTION" era in realtà un sotto (in quanto le costanti di solito sono sottose in perl), quindi abbiamo provato il test successivo. cambierò anche il titolo – Seki

risposta

9

B::Deparse salvato:

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 

Il MB_ICONQUESTION chiamata nel primo caso è considerata una chiamata di funzione con gli argomenti +4, 'hello'. Nel secondo caso, è considerato come una chiamata di funzione senza argomenti e con l'aggiunta di 4. Non è una costante, sembra, ma una funzione.

Nel codice sorgente otteniamo questa verificate:

sub MB_ICONQUESTION      { 0x00000020 } 

Esso è una funzione che restituisce 32 (00100000 in binario, che indica un bit è impostato). Inoltre, come indicato da Sobrique, questa è una variabile flag, quindi non si dovrebbe usare addizione, ma gli operatori logici e/o bit a bit.

Nel tuo caso, accetta solo argomenti e li ignora. Questo è un po 'di confusione se ti aspetti una costante.

Nel tuo caso esperimento, la dichiarazione

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

viene interpretato

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

perché l'esecuzione da destra a sinistra va

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

Perché più + ha una maggiore precedence a virgola , e unario + ancora più alto.

Per disambiguare, è necessario fare utilizzare le parentesi per chiarire la precedenza:

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

Come regola generale, si dovrebbe sempre usare le parentesi con le chiamate di subroutine. In perldoc perlsub ci sono 4 notazioni di chiamata:

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

Di cui a mio parere, solo il primo è trasparente, e gli altri un po 'oscure.

+5

In definitiva un bug nel modulo che fornisce la "costante", che ha dimenticato di dare il prototipo '()' che esiste per risolvere esattamente questo problema. – LeoNerd

+2

O semplicemente "usa costante", che penso gestisca anche correttamente. – Sobrique

+0

@LeoNerd I prototipi sono più spesso il problema della soluzione. Esistono per dare la possibilità di far sì che le subroutine agiscano come built-in, non per il controllo degli argomenti. – TLP

5

Questo è tutto a che fare con il modo in cui stai invocando T e come perl sta interpretando i risultati.

Se Deparse tuo esempio otteniamo:

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

Questo chiaramente non è quello che hai in mente, ma non spiega il motivo per cui si ottiene il risultato che fate.

vorrei suggerire in esempio originale - piuttosto che + si potrebbe desiderare di considerare l'utilizzo di | come sembra molto simile MB_ICONQUESTION è destinato ad essere una bandiera.

Quindi:

use strict; 
use warnings; 

use Win32 qw(MB_ICONQUESTION); 

print MB_ICONQUESTION; 

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

O

use strict; 
use warnings; 

use Win32 qw(MB_ICONQUESTION); 

print MB_ICONQUESTION; 

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

producono lo stesso risultato.

Questo è causa di precence quando si richiama subroutine senza staffe - si può fare:

print "one", "two"; 

Ed entrambi sono trattati come argomenti a print. Perl assume che gli argomenti dopo un sub devono essere passati ad esso.

+4 viene enumerato come argomento e passato a T.

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

test 1; 
test +1; 

Se Deparse questo, vediamo tratta Perl come:

test 1; 
test 1; 

Quindi in ultima analisi - c'è un bug in Win32 che avete trovato, che sarebbe risolvibile da:

sub MB_ICONQUESTION() {0x00000020} 

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

O forse:

use constant MB_ICONQUESTION => 0x00000020; 

O r come notato - la soluzione del codice - non utilizzare + e utilizzare invece | che avrà lo stesso risultato per le operazioni di bit flag, ma a causa della precedenza degli operatori non verrà mai passato nella subroutine. (Ovviamente, specifica sempre la parentesi per le tue costanti)

+0

Grazie per il suggerimento di usare binary 'OR' invece dell'aggiunta, btw, non ho pensato di usare la parentesi esplicita perché all'inizio non sapevo che la costante API non fosse in realtà una costante ma una sub ... Io sono kinda a Perl newcomer: o) – Seki

+0

Tutti iniziano una lingua all'inizio. Metti insieme una domanda eccellente, che è molto apprezzata, interessante e con sufficiente profondità per riprodurla e investigare. – Sobrique