Mendenhall
Mendenhall

Reputation: 3

Argument passed to Perl VERSION subroutine during module version check

I define a Perl module, like so:

#!/usr/bin/env perl
use strict;
use warnings;

package Sample;
use Data::Dumper; 
our $VERSION = v1.10;

sub VERSION
{
    my ($class, $version) = @_;
    print ("version is $version\n");
    print Dumper ($version);
}

The nature of the value passed in $version changes depending on how the module is imported:

$ perl -e 'use Sample 1.0'
version is 1
$VAR1 = '1';

However, if the required module version is specified as a v-string:

$ perl -e 'use Sample v1.0'
version is 
$VAR1 = v1.0;

What data type is being passed in $version in the second case? It's apparently not a simple scalar, and it's not a reference.

Upvotes: 0

Views: 48

Answers (2)

ikegami
ikegami

Reputation: 385789

Each dot-separated number is converted into a character with the ordinal value of the number.[1] In other words,

v1.0   ≡   "\x01\x00"   ≡   chr(1).chr(0)   ≡   pack('W*', 1, 0)

You can convert a v-string into something human readable using the %vd format specifier of sprintf.[2]

$ perl -e'CORE::say sprintf("%vd", v1.0)'
1.0

But it's better to use the version module.

$ perl -Mversion -e'CORE::say version->parse(v1.0)'
v1.0

It's better because the version module can handle version strings in general (not just v-strings).

$ perl -Mversion -e'
   my $v1 = version->parse(1.0);
   my $v2 = version->parse("1.0");
   my $v3 = version->parse(v1.0);
   my $v4 = version->parse("v1.0");
   CORE::say "equal"
      if $v1 == $v2
      && $v1 == $v3
      && $v1 == $v4
'
equal

One can use any numerical or string comparison operator[3] to compare version objects.


  1. It's more than that, though. A scalar containing a v-string has magic (of type V) applied, so it's possible to dectect that it's a v-string.

    $ perl -MDevel::Peek -e'Dump("\x01\x00"); Dump(v1.0);'
    SV = PV(0xbc9d70) at 0xbe7998
      REFCNT = 1
      FLAGS = (POK,IsCOW,READONLY,PROTECT,pPOK)
      PV = 0xbf1ed0 "\1\0"\0
      CUR = 2
      LEN = 10
      COW_REFCNT = 0
    SV = PVMG(0xc20480) at 0xbe7938
      REFCNT = 1
      FLAGS = (RMG,POK,IsCOW,READONLY,PROTECT,pPOK)
      IV = 0
      NV = 0
      PV = 0xbf0190 "\1\0"\0
      CUR = 2
      LEN = 10
      COW_REFCNT = 0
      MAGIC = 0xbf3a80
        MG_VIRTUAL = 0
        MG_TYPE = PERL_MAGIC_vstring(V)
        MG_LEN = 4
        MG_PTR = 0xbf1700 "v1.0"
    

    This magic is even applied to any scalar to which the v-string is copied!

    $ perl -MDevel::Peek -e'my $v1 = v1.0; my $v2 = $v1; Dump($v2)'
    SV = PVMG(0x9dc500) at 0x9a3a00
      REFCNT = 1
      FLAGS = (RMG,POK,IsCOW,pPOK)
      IV = 0
      NV = 0
      PV = 0x9ac1b0 "\1\0"\0
      CUR = 2
      LEN = 10
      COW_REFCNT = 2
      MAGIC = 0x9b8090
        MG_VIRTUAL = 0
        MG_TYPE = PERL_MAGIC_vstring(V)
        MG_LEN = 4
        MG_PTR = 0x9adef0 "v1.0"
    

    I believe the version module takes advantage of this information.

  2. This format specifier works on any string, so it's convenient for checking for hidden or special characters when debugging.

    $ perl -e'CORE::say sprintf "%v02X", "abc\r\n"'
    61.62.63.0D.0A
    
  3. ==, <, >, <=, >=, <=>, eq, lt, gt, le, ge and cmp.

Upvotes: 1

JGNI
JGNI

Reputation: 4013

A v string is a string. Each number is assumed to be a Unicode code point and is converted to that character so what you are actually printing out is chr(1) . chr(0). You can prove this with the following script

my $vstring = v80.101.114.108
print $vstring, "\n";

This will print Perl

Upvotes: 2

Related Questions