Ralesk has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed Monks,

I was dumb and accidentally anded some flags instead of orring them at some point when I wrote the part of the software that employs Win32::Sound — I would have found out way earlier, had SND_ASYNC not been 1, and so silently working as intended...

Today I found the bug, replaced things with bitwise ors, and wondered why this could ever have worked before, and it seems that in weird situations (such as the flags being used in the argument list of Win32::Sound::Play), the constants behave completely non-DWIMly.

So here's some examples:

C:\>perl -E "use Win32::Sound; say Win32::Sound::SND_ASYNC; say Win32: +:Sound::SND_LOOP; say Win32::Sound::SND_NODEFAULT;" 1 8 2

This should mean that SND_ASYNC and (as I erroneously wrote) SND_NODEFAULT is 0, right? 1 & 2 is zero, we all know that. Those two orred together is 3.

... Win32::Sound::Play($filename, Win32::Sound::SND_ASYNC & Win32::Sound:: +SND_NODEFAULT); ...

Lo and behold, that thing there has SND_ASYNC set. No, it's not a precedence issue, it would be the same if the bit construct were surrounded by an extra set of parentheses.

C:\>perl -E "use Win32::Sound; say Win32::Sound::SND_ASYNC & Win32::So +und::SND_NODEFAULT;" 1 C:\>perl -E "use Win32::Sound; $x = (Win32::Sound::SND_ASYNC & Win32:: +Sound::SND_NODEFAULT); say $x" 1 C:\>perl -E "use Win32::Sound; say (1 & 2);" 0 C:\>perl -E "use Win32::Sound; say ('1' & '2');" 0 C:\>perl -E "use Win32::Sound; my $as = Win32::Sound::SND_ASYNC; my $n +d = Win32::Sound::SND_NODEFAULT; say $as & $nd;" 0 C:\>perl -E "use Win32::Sound; my @as = Win32::Sound::SND_ASYNC; my @n +d = Win32::Sound::SND_NODEFAULT; say @as; say @nd; say (@as & @nd);" 1 2 1

There it is — & requires scalar values to bitwise and, so takes the length of the two list context return values, which is 1 for each because each is a one element array. Or, if anything, forcing list context on these guys seems to at least replicate the error — no clue why they'd be in list context there in the original usage!

This doesn't, however, explain why it works out of the box with |, where the list context hack doesn't:

C:\>perl -E "use Win32::Sound; my @as = Win32::Sound::SND_ASYNC; my @n +d = Win32::Sound::SND_NODEFAULT; say @as | @nd;" 1 C:\>perl -E "use Win32::Sound; $x = (Win32::Sound::SND_ASYNC | Win32:: +Sound::SND_NODEFAULT); say $x" 3

The only question I have is just: why?

Replies are listed 'Best First'.
Re: Win32::Sound constants are weird (autoloaded, not actual constants)
by beech (Parson) on Dec 16, 2017 at 02:18 UTC

    Hi,

    Those don't look to be actual constants in the perl sense(empty prototype)/use constant

    $ perl -MO=Deparse -MWin32::Sound -e " print Win32::Sound::SND_NODEFAU +LT & Win32::Sound::SND_ASYNC " use Win32::Sound; print Win32::Sound::SND_NODEFAULT(&Win32::Sound::SND_ASYNC); -e syntax OK

    Its one of the reasons I tend to always use () for anything called constants in the docs

    $ perl -MWin32::Sound -e " print Win32::Sound::SND_NODEFAULT() & Win3 +2::Sound::SND_ASYNC() " 0

    update: after looking at the source, I see they're also autoloaded using some old ExtUtils::Constant code ... and there is a warning about that in https://metacpan.org/pod/Exporter#AUTOLOADed-Constants

    You might wish to report this bug to rt://Win32-Sound, all that autoload stuff for 20 constants seems like it belongs in the past :)