Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

making $^V a version object broke functionality #15488

Open
p5pRT opened this issue Jul 31, 2016 · 13 comments
Open

making $^V a version object broke functionality #15488

p5pRT opened this issue Jul 31, 2016 · 13 comments

Comments

@p5pRT
Copy link

@p5pRT p5pRT commented Jul 31, 2016

Migrated from rt.perl.org#128786 (status was 'open')

Searchable as RT128786$

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 31, 2016

From @mauke

Created by @mauke

perldoc perl5100delta​:

| $^V is now a "version" object instead of a v-string
| $^V can still be used with the %vd format in printf, but any
| character-level operations will now access the string representation of
| the "version" object and not the ordinals of a v-string. Expressions like
| "substr($^V, 0, 2)" or "split //, $^V" no longer work and must be
| rewritten.

OK, they must be rewritten. But how?

Consider e.g.

  printf "use feature '​:%vd';\n", substr($^V, 0, 2);

This is reasonable code with v-strings. How do you do the same thing with a
version object?

As far as I can see, the version module only provides parsing, pretty-printing,
and comparison. There's no way to access version components (e.g. if you want
to determine the major version of the perl you're running in).

The 5.10 change removed functionality, telling us that the 5.6/5.8 way no
longer works, but without providing any replacement.

As an aside, perldoc -v '$^V' says​:

| The disadvantage is that prior to v5.10.0, it was only a literal v-string,
| which can't be easily printed or compared.

Which doesn't have any technical basis​: You could easily print it using
'printf "%vd\n", $^V' and you could easily compare it using '$^V ge v5.8', just
like now.

Perl Info

Flags:
    category=core
    severity=low

Site configuration information for perl 5.24.0:

Configured by mauke at Mon May  9 21:21:33 CEST 2016.

Summary of my perl5 (revision 5 version 24 subversion 0) configuration:
   
  Platform:
    osname=linux, osvers=4.4.5-1-arch, archname=i686-linux
    uname='linux simplicio 4.4.5-1-arch #1 smp preempt thu mar 10 07:54:30 cet 2016 i686 gnulinux '
    config_args=''
    hint=previous, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -flto',
    cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion='', gccversion='6.1.1 20160501', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234, doublekind=3
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12, longdblkind=3
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-fstack-protector-strong -L/usr/local/lib -flto'
    libpth=/usr/local/lib /usr/lib/gcc/i686-pc-linux-gnu/6.1.1/include-fixed /usr/lib /lib /usr/local/lib /usr/lib/gcc/i686-pc-linux-gnu/6.1.1/include-fixed /usr/lib
    libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.23.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.23'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -flto -L/usr/local/lib -fstack-protector-strong'



@INC for perl 5.24.0:
    /home/mauke/usr/lib/perl5/site_perl/5.24.0/i686-linux
    /home/mauke/usr/lib/perl5/site_perl/5.24.0
    /home/mauke/usr/lib/perl5/5.24.0/i686-linux
    /home/mauke/usr/lib/perl5/5.24.0
    .


Environment for perl 5.24.0:
    HOME=/home/mauke
    LANG=en_US.UTF-8
    LANGUAGE=en_US
    LC_COLLATE=C
    LC_MONETARY=de_DE.UTF-8
    LC_TIME=de_DE.UTF-8
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/mauke/perl5/perlbrew/bin:/home/mauke/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
    PERLBREW_BASHRC_VERSION=0.73
    PERLBREW_HOME=/home/mauke/.perlbrew
    PERLBREW_ROOT=/home/mauke/perl5/perlbrew
    PERL_BADLANG (unset)
    PERL_UNICODE=SAL
    SHELL=/bin/bash

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 31, 2016

From @JohnPeacock

On 07/31/2016 10​:04 AM, l.mai@​web.de wrote​:

# New Ticket Created by l.mai@​web.de
# Please include the string​: [perl #128786]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=128786 >

This is a bug report for perl from l.mai@​web.de,
generated with the help of perlbug 1.40 running under perl 5.24.0.

-----------------------------------------------------------------
[Please describe your issue here]

perldoc perl5100delta​:

| $^V is now a "version" object instead of a v-string
| $^V can still be used with the %vd format in printf, but any
| character-level operations will now access the string representation of
| the "version" object and not the ordinals of a v-string. Expressions like
| "substr($^V, 0, 2)" or "split //, $^V" no longer work and must be
| rewritten.

OK, they must be rewritten. But how?

Consider e.g.

printf "use feature '&#8203;:%vd';\\n"\, substr\($^V\, 0\, 2\);

Perl objects are typically just hashes of stuff, blessed into a class, e.g.

  $ perl -w -MData​::Dumper -E 'say Dumper($^V);'
  $VAR1 = bless( {
  'original' => 'v5.20.2',
  'version' => [
  5,
  20,
  2
  ],
  'qv' => 1
  }, 'version' );

So your code could be rewritten as​:

  printf "use feature '​:%d.$d;\n", @​{$^V->{version}}[0,1];

v-strings are intrinsically a binary format, so you MUST treat it
specially when trying to print or compare. Version objects can be
treated as a normal scalar and printed/compared using non-special methods.

If you'd like to open a feature request for version.pm to include
methods to extract the epoch/major/minor/release from a version objects,
I'd be happy to add one. This is the first time in nearly 10 years that
anyone has requested such a thing, but it is easy enough to add.

Thanks

John

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 31, 2016

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Jul 31, 2016

From @mauke

Am 31.07.2016 um 17​:48 schrieb John Peacock​:

On 07/31/2016 10​:04 AM, l.mai@​web.de wrote​:

# New Ticket Created by l.mai@​web.de
# Please include the string​: [perl #128786]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=128786 >

Perl objects are typically just hashes of stuff, blessed into a class, e.g.

$ perl -w -MData​::Dumper -E 'say Dumper($^V);'
$VAR1 = bless( {
'original' => 'v5.20.2',
'version' => [
5,
20,
2
],
'qv' => 1
}, 'version' );

So your code could be rewritten as​:

printf "use feature '​:%d.$d;\n", @​{$^V->{version}}[0,1];

That's not part of the public API. I'd have to rely on implementation
details that might change at any time.

Can this code be written at all using just the public API and without
reparsing the pretty-printed version number? (E.g. it's easy enough to
do 'my @​v = split /\./, sprintf "%vd", $^V;' but that's cheating. And a
bit awful.)

v-strings are intrinsically a binary format, so you MUST treat it
specially when trying to print or compare. Version objects can be
treated as a normal scalar and printed/compared using non-special methods.

1. v-strings are normal scalars​: they're strings.
2. No special treatment is required for comparing v-strings. v-string
comparison is normal string comparison.
3. Special treatment is required for comparing version objects. version
object comparison is not normal string comparison, and inadvertently
stringifying a version object and then comparing it leads to wrong results.
4. Pretty-printing a version number is rare in my code. Most of the time
I only want to compare versions, extract components, and construct new
versions (e.g. for automatically incrementing a version number, etc).

If you'd like to open a feature request for version.pm to include
methods to extract the epoch/major/minor/release from a version objects,
I'd be happy to add one. This is the first time in nearly 10 years that
anyone has requested such a thing, but it is easy enough to add.

I'm not directly concerned with version.pm. I've never had a use for it.

My main concern is being able to introspect the version number of the
currently running perl. If that has to go through version.pm, OK.

In that case a method to return the numeric components would be nice, as
well as a method to get the v-string representation​:

  my @​parts = $^V->parts; # (5, 24, 0)
  my $str = $^V->vstring; # "\x05\x18\x00"

Would that be good addition to the version.pm API?

--
Lukas Mai <plokinom@​gmail.com>

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 1, 2016

From zefram@fysh.org

Lukas Mai wrote​:

My main concern is being able to introspect the version number of the
currently running perl.

$] has always worked for that, and still does.

  @​parts = map { 0+$_ } sprintf("%010.6f", "$]") =~ /([0-9]{3})/g;

-zefram

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 3, 2016

From @JohnPeacock

On 07/31/2016 03​:46 PM, Lukas Mai wrote​:

Am 31.07.2016 um 17​:48 schrieb John Peacock​:

So your code could be rewritten as​:

printf "use feature '​:%d.$d;\n", @​{$^V->{version}}[0,1];

That's not part of the public API. I'd have to rely on implementation
details that might change at any time.

Actually, the internal representation *is* part of the public API, see
version​::Internals. The version.pm code is also specifically designed
to be easy to subclass, so you would be free to implement your own
method calls to do whatever you want (like generating a v-string form).

1. v-strings are normal scalars​: they're strings.

I beg to differ. v-strings are stored as simple scalar but they are an
array of code points, usually consisting of what would be non-printable
control characters. For example, the current Perl release is

  ENQ CAN NUL

You cannot print a v-string without the special "%vd" format, which
presupposes that you actually know you have a v-string in the first
place. Originally, v-strings were a lossy parsing artifact, with no way
of distinguishing 53.56.48 from "5.8.0". After Perl v5.8.1 was
released, v-strings became a magical type, so you with introspection you
could distinguish them from normal strings​:

$ perl -MDevel​::Peek -E 'say Dump(53.56.48)'
SV = PVMG(0x13b0b50) at 0x1366eb8
  REFCNT = 1
  FLAGS = (RMG,POK,READONLY,IsCOW,pPOK)
  IV = 0
  NV = 0
  PV = 0x136bd10 "580"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 0
  MAGIC = 0x137e8e0
  MG_VIRTUAL = 0
  MG_TYPE = PERL_MAGIC_vstring(V)
  MG_LEN = 8
  MG_PTR = 0x136a740 "53.56.48"

2. No special treatment is required for comparing v-strings. v-string
comparison is normal string comparison.

While this is true, it is also misleading, in that you had to already
know you were dealing with a v-string to construct your test. Most
people found this confusing and used $] instead, which has its own set
of problems.

3. Special treatment is required for comparing version objects. version
object comparison is not normal string comparison, and inadvertently
stringifying a version object and then comparing it leads to wrong results.

Then frankly you are using the version module wrong. A version object
can be trivially compared against either a
string-that-looks-like-a-version, a bare number, or another
version-derived object using either numeric or string comparison
operators (> and gt). If you force stringification, of course you lose
the object methods.

4. Pretty-printing a version number is rare in my code. Most of the time
I only want to compare versions, extract components, and construct new
versions (e.g. for automatically incrementing a version number, etc).

The fact that the normal stringification method of version objects is to
print in a consistent normal form is just gravy. The real reason they
exist in the first place is to treat versions as first class objects,
with consistent behavior. To be honest, you are the first person to
request being able extract the elements of the version for further
processing. Most people appear to want to treat versions as fixed values.

In that case a method to return the numeric components would be nice, as
well as a method to get the v-string representation​:

my @&#8203;parts = $^V\->parts;    \# \(5\, 24\, 0\)
my $str   = $^V\->vstring;  \# "\\x05\\x18\\x00"

Would that be good addition to the version.pm API?

Attached is a proposed addition to the public API that provides the
elements() method to return the array ref, as well as helper functions
to print out the epoch/major/minor/patch for any given version object
(including $^V). Since this has to replace the internal Perl
implementation of version.pm with the additional methods, you would have
to include

  use version;

before you can use any of these methods. If this would fit your needs,
I'd be happy to release a new version.pm to CPAN and this functionality
would be included in any new Perl releases.

John

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 3, 2016

From @JohnPeacock

elements.diff
diff -r ba402aa3d3a5 lib/version.pm
--- a/lib/version.pm	Wed Aug 03 05:22:30 2016 -0400
+++ b/lib/version.pm	Wed Aug 03 06:31:20 2016 -0400
@@ -124,5 +124,36 @@
     }
 }
 
+# Helper methods
+sub elements {
+    my ($self) = @_;
+    return ($self->{version});
+}
 
+sub epoch {
+    my ($self) = @_;
+    my @elements = @{$self->elements};
+    return 'v'.$elements[0];
+}
+
+sub major {
+    my ($self) = @_;
+    my @elements = @{$self->elements};
+    push @elements, 0 while $#elements < 2;
+    return 'v'.join('.', @elements[0,1]);
+}
+
+sub minor {
+    my ($self) = @_;
+    my @elements = @{$self->elements};
+    push @elements, 0 while $#elements < 3;
+    return 'v'.join('.', @elements[0,1,2]);
+}
+
+sub patch {
+    my ($self) = @_;
+    my @elements = @{$self->elements};
+    push @elements, 0 while $#elements < 4;
+    return 'v'.join('.', @elements[0,1,2,3]);
+}
 1;
@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 3, 2016

From @mauke

Am 03.08.2016 um 12​:39 schrieb John Peacock​:

On 07/31/2016 03​:46 PM, Lukas Mai wrote​:

Am 31.07.2016 um 17​:48 schrieb John Peacock​:

So your code could be rewritten as​:

printf "use feature '​:%d.$d;\n", @​{$^V->{version}}[0,1];

That's not part of the public API. I'd have to rely on implementation
details that might change at any time.

Actually, the internal representation *is* part of the public API, see
version​::Internals.

Wait, version​::Internals is supposed to be a public API?

You definitely need to document that, then. When something is called
"internals", I assume it's purely informational and describes a snapshot
(the current state of the implementation) with no guarantees about
stability or compatibility.

Case in point​: the $v->{version} field is described in a document called
"version​::Internals", in the section "IMPLEMENTATION DETAILS", in the
subsection "Version Object Internals", and labeled "[t]he internal
structure of version objects".

All of this screams "DO NOT USE" (outside of debugging) to me.

The version.pm code is also specifically designed
to be easy to subclass, so you would be free to implement your own
method calls to do whatever you want (like generating a v-string form).

Again, this should be documented, then. Neither version nor
version​::Internals say anything about subclasses, base classes, or
inheritance.

(Besides, I'd have to rebless $^V, which is a whole nother can of worms.)

2. No special treatment is required for comparing v-strings. v-string
comparison is normal string comparison.

While this is true, it is also misleading, in that you had to already
know you were dealing with a v-string to construct your test. Most
people found this confusing and used $] instead, which has its own set
of problems.

I find the current situation even more confusing. "v-strings are just
arrays of codepoints" was simple and made sense to me. (I wouldn't be
surprised if people still mostly used $] because it's just a number - no
surprises with overloading.)

3. Special treatment is required for comparing version objects. version
object comparison is not normal string comparison, and inadvertently
stringifying a version object and then comparing it leads to wrong
results.

Then frankly you are using the version module wrong. A version object
can be trivially compared against either a
string-that-looks-like-a-version, a bare number, or another
version-derived object using either numeric or string comparison
operators (> and gt). If you force stringification, of course you lose
the object methods.

I'm not using the version module (directly). I'm using the $^V variable,
which used to be a string (it was when I learned Perl) so it made no
difference whether I used $^V or "$^V".

A few days ago I had to (for the first time) do something with perl
versions that wasn't just a simple inequality check, for which I'd've
used $]. So this was how I ended up looking into how $^V actually works
nowadays. (I realize I'm about 10 years late with my complaints.)

4. Pretty-printing a version number is rare in my code. Most of the time
I only want to compare versions, extract components, and construct new
versions (e.g. for automatically incrementing a version number, etc).

The fact that the normal stringification method of version objects is to
print in a consistent normal form is just gravy. The real reason they
exist in the first place is to treat versions as first class objects,
with consistent behavior. To be honest, you are the first person to
request being able extract the elements of the version for further
processing. Most people appear to want to treat versions as fixed values.

($] works fine for fixed values. Why bother with an object then?)

My actual use case is this​: I have a project (stored in a single
repository) that I want to be able to run under different perl versions.
There's a module (pdflib) that's specific to the perl version, i.e.
different perls require different versions of the module.

My solution was to create a wrapper module that adds a version-dependent
subdirectory to @​INC, then loads the real module. That way all variants
of the module can be added to the repository together, and perl
automatically selects the right variant at execution time.

Further constraints​: I want the subdirectory names to look nice for
human readers (i.e. no "5.018002" or similar), and the minor version
should be dropped because only the major version of perl5 matters. I
went with "v5.12", "v5.14", etc. (Accidental bonus​: The '.' means I
can't accidentally load a module from this subdirectory via 'use
v5_12​::whatever' because no bareword can contain '.'.)

A while later I came across a second use case​: Create a dynamic script
template (e.g. for use with vim) that automatically inserts 'use feature
"​:5.XX";' at the top of new files, where 5.XX is the version of the
current perl.

Both of these are easy with v-strings​: sprintf('v%vd', substr($v, 0, 2))
and sprintf('use feature "​:%vd";', substr($v, 0, 2)), respectively.

In that case a method to return the numeric components would be nice, as
well as a method to get the v-string representation​:

my @&#8203;parts = $^V\->parts;    \# \(5\, 24\, 0\)
my $str   = $^V\->vstring;  \# "\\x05\\x18\\x00"

Would that be good addition to the version.pm API?

Attached is a proposed addition to the public API that provides the
elements() method to return the array ref, as well as helper functions
to print out the epoch/major/minor/patch for any given version object
(including $^V). Since this has to replace the internal Perl
implementation of version.pm with the additional methods, you would have
to include

use version;

before you can use any of these methods. If this would fit your needs,
I'd be happy to release a new version.pm to CPAN and this functionality
would be included in any new Perl releases.

You seem to have a (harmless) off-by-one error in there​: You do 'push
@​elements, 0 while $#elements < 2' (which ensures @​elements has at least
3 elements) but then you only access $elements[0] and $elements[1], as
if the condition had been '... while @​elements < 2'.

The other issue I see is that the methods and names are specific to $^V,
i.e. the way perl uses its version numbers. They're not necessarily
right for a generic version object.

Finally, the epoch/major/minor/patch methods form yet another
pretty-printing API, which version.pm already has. (The 'major' method
happens to fit my first use case exactly, but doesn't help with the
other one ('use feature').) I'd rather have the building blocks to make
my own pretty-printer.

I like 'elements' but from the name I would've expected it to return a
list, not a reference to an array.

--
Lukas Mai <plokinom@​gmail.com>

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 3, 2016

From @mauke

Am 01.08.2016 um 08​:38 schrieb Zefram​:

Lukas Mai wrote​:

My main concern is being able to introspect the version number of the
currently running perl.

$] has always worked for that, and still does.

@&#8203;parts = map \{ 0\+$\_ \} sprintf\("%010\.6f"\, "$\]"\) =~ /\(\[0\-9\]\{3\}\)/g;

I think that's borderline obfuscated. The biggest WTF in there is "$]"​:
Why are you explicitly converting a floating-point number to a string,
only to have it be implicitly reinterpreted as a float by %f, which then
explicitly stringifies it again?

Also, that still counts as reparsing a pretty-printed form. If I wanted
that, I could do it more directly with

  @​parts = $^V =~ /\d+/g;

(which silently fails in 5.6 and 5.8) or

  @​parts = sprintf("%vd", $^V) =~ /\d+/g;
  # or
  # @​parts = split /\./, sprintf "%vd", $^V;

(for compatibility).

--
Lukas Mai <plokinom@​gmail.com>

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 3, 2016

From zefram@fysh.org

Lukas Mai wrote​:

I think that's borderline obfuscated.

It's arcane, yes. But reliable.

                                 The biggest WTF in there is "$\]"&#8203;: Why

are you explicitly converting a floating-point number to a string, only to
have it be implicitly reinterpreted as a float by %f,

To work around [perl #72210].

                                                 which then explicitly

stringifies it again?

The sprintf standardises the pattern of digits into what's needed for
pulling the parts apart.

-zefram

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 4, 2016

From @ap

* Lukas Mai <plokinom@​gmail.com> [2016-07-31 21​:48]​:

My main concern is being able to introspect the version number of the
currently running perl. If that has to go through version.pm, OK.

Just use $]. $^V is some hypey modernist nonsense boondoggle. $] is
simple and clearly defined, string comparisons of its values Just Work,
it’s plain and obvious. It’s even nicer than the niceness of v-strings
you seem quite taken with.

The only annoyance you might have is that you get '5.022001' instead of
'5.22.1' if you print it verbatim (which you can’t do with v-strings
either). That was the whole cause of a gigantic goose chase.

However, don’t listen to this​:

* Zefram <zefram@​fysh.org> [2016-08-01 08​:48]​:

@&#8203;parts = map \{ 0\+$\_ \} sprintf\("%010\.6f"\, "$\]"\) =~ /\(\[0\-9\]\{3\}\)/g;

… because $] is not a float, and you get in trouble (that the manual
explicitly warns you about!) if you try to treat it that way.

  $ perl -MDevel​::Peek -e 'Dump $]'
  SV = PV(0x7fd55b801280) at 0x7fd55b826790
  REFCNT = 1
  FLAGS = (POK,READONLY,pPOK)
  PV = 0x7fd55b40ee20 "5.016001"\0
  CUR = 8
  LEN = 16

It’s a string. And always has been. Treat it that way and it will treat
you well. Specifically it’s always been a string with some digits – more
specifically, always a single digit '5' – plus a dot and then 6 digits
after that. So instead of that borderline-obfuscated stuff, you ought to
write some variation on (the maximally future-paranoid)

  @​parts = map 0+$_, $] =~ /\A(\d+)\.(\d{3})(\d{3})\z/gaa;

But for your stated main concern, I would instead go with

  printf "use feature '​:5.%d';\n", substr $], 2, 3;

That’s about as simple as anything gets, aye?

Regards,
--
Aristotle Pagaltzis // <http​://plasmasturm.org/>

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 4, 2016

From zefram@fysh.org

Aristotle Pagaltzis wrote​:

specifically, always a single digit '5' -- plus a dot and then 6 digits
after that.

No, it's not of that form on every version. On 5.8.0 it's "5.008",
only three digits after the dot. And it's not always been constructed
as a string; these shorter strings arise by it being constructed as an
NV and converted. The string form is certainly the form to work from,
because the NV value used to be constructed badly, but it is necessary
to be more forgiving about the digit pattern. My sprintf formulation
is a valid way of canonicalising the digit pattern.

-zefram

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Aug 19, 2016

From @JohnPeacock

On 08/03/2016 03​:49 PM, Lukas Mai wrote​:

Am 03.08.2016 um 12​:39 schrieb John Peacock​:

On 07/31/2016 03​:46 PM, Lukas Mai wrote​:

Am 31.07.2016 um 17​:48 schrieb John Peacock​:

So your code could be rewritten as​:

printf "use feature '​:%d.$d;\n", @​{$^V->{version}}[0,1];

That's not part of the public API. I'd have to rely on implementation
details that might change at any time.

Actually, the internal representation *is* part of the public API, see
version​::Internals.

Wait, version​::Internals is supposed to be a public API?

You definitely need to document that, then. When something is called
"internals", I assume it's purely informational and describes a snapshot
(the current state of the implementation) with no guarantees about
stability or compatibility.

The version​::Internals text originally was part of the main POD and I
split it out to make the main documentation more applicable for
consumers of the module. I originally added the documentation for how
the object was structured specifically so anyone was free to extend it.
I realize now that I don't actually say that, so mea culpa.

I actually test the ability to subclass version.pm part of the
regression suite; I run all tests with the base class and again with a
trivial subclass.

So here is an example subclass just for you​:


package vversion;
use base "version";

sub vstring {
  my ($self) = @​_;
  my $vs = $self->stringify();
  return eval("$vs");
}

1;


Call it like this​:

$ perl -I. -Mvversion -MDevel​::Peek -E \
  '$v = vversion->new($^V)->vstring(); say Dump($v);'

SV = PVMG(0x15ee440) at 0x1625c68
  REFCNT = 1
  FLAGS = (RMG,POK,IsCOW,pPOK)
  IV = 0
  NV = 0
  PV = 0x1611eb0 "\5\26\1"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 0
  MAGIC = 0x15f4650
  MG_VIRTUAL = 0
  MG_TYPE = PERL_MAGIC_vstring(V)
  MG_LEN = 7
  MG_PTR = 0x16079e0 "v5.22.1"

That gets you the vstring that you can do anything you want with. In
fact, you don't even need to use the subclass; you can just use the same
technique in your code​:

$ perl -MDevel​::Peek -E \
  '$v = $^V->stringify; $v = eval("$v"); say Dump($v);'

SV = PVMG(0x247d920) at 0x24b4c68
  REFCNT = 1
  FLAGS = (RMG,POK,pPOK)
  IV = 0
  NV = 0
  PV = 0x2484230 "\5\26\1"\0
  CUR = 3
  LEN = 10
  MAGIC = 0x2498530
  MG_VIRTUAL = 0
  MG_TYPE = PERL_MAGIC_vstring(V)
  MG_LEN = 7
  MG_PTR = 0x24a0f10 "v5.22.1"

Since you seem to be primarily interested in the value of $^V, you can
skip all of that and go straight to the eval​:

$ perl -MDevel​::Peek -E '$v = eval("$^V"); say Dump($v);'
SV = PVMG(0x2308cd0) at 0x22bd210
  REFCNT = 1
  FLAGS = (RMG,POK,IsCOW,pPOK)
  IV = 0
  NV = 0
  PV = 0x2307240 "\5\26\1"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 0
  MAGIC = 0x22c1fd0
  MG_VIRTUAL = 0
  MG_TYPE = PERL_MAGIC_vstring(V)
  MG_LEN = 7
  MG_PTR = 0x22ae060 "v5.22.1"

All things considered, I think this is not something that absolutely
requires fixing version.pm or the equivalent core code.

John

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant