Mr. Llama
Mr. Llama

Reputation: 20899

Version-dependent fallback code

I have a script that needs to run on multiple servers, however, each server may not have the same version of Perl available and may have differing features.

Perl v5.14 introduced the /r modifier for regular expressions which returns the result of a substitution and leaves the original text alone. If that is not available, I would like to use some fallback code instead.

Here's an example:

#!/usr/bin/perl

# Don't use cryptic variable names
use English;

my $str = "Hello, World!";
my $search = ',';

if ($PERL_VERSION ge v5.14) {
    print "Using version that supports /r modifier\n";

    # Perl v5.14 introduced the /r flag for regular expressions
    # From the perldoc: r  - perform non-destructive substitution and return the new value
    print "After replacement: " . ($str =~ s/\Q${search}\E/_/gr) . $/;
    print "Original string: " . $str . $/;

} else {
    print "This version does not support the /r modifier\n";

    # Prior to Perl v5.14, no /r option existed and the original string will be clobbered
    # To deal with this, we need to make a copy first, then modify the copy instead
    my $str_copy = $str;
    $str_copy =~ s/\Q${search}\E/_/g;

    print "After replacement: " . $str_copy . $/;
    print "Original string: " . $str . $/;
}

When Perl v5.14 is available, running the script gives me the desired results:

$ ./version_dependent.pl
Using version that supports /r modifier
After replacement: Hello_ World!
Original string: Hello, World!

When a version less than v5.14 is used, I get a syntax error because of the r:

$ ./version_dependent.pl
Bareword found where operator expected at ./version_dependent.pl line 15, near "s/\Q${search}\E/_/gr"
syntax error at ./version_dependent.pl line 15, near "s/\Q${search}\E/_/gr"
Execution of ./version_dependent.pl aborted due to compilation errors.

What I would like to get is:

$ ./version_dependent.pl
This version does not support the /r modifier
After replacement: Hello_ World!
Original string: Hello, World!

Is there any way to get the script to behave as intended?
If this were a C-like language, I handle the issue using the preprocessor, but I don't think Perl has that functionality.


Edit: The above code is just an example.
In the actual application, there are features I'm using from 5.14 for performance reasons. I can manually recreate the functionality in versions less than 5.14, but the performance suffers.

Upvotes: 3

Views: 182

Answers (2)

mob
mob

Reputation: 118605

Many recent features are not forward-compatible. As you've seen, you'll get compile-time errors using a feature that is too new for the version of perl you are running. Using block-eval won't help, because the contents of the block need to be valid for the current perl interpreter.

You are on the right track with checking for the current perl version and branching. The $] variable is one mechanism to do that. Checking $Config{PERL_VERSION} (after you use Config) is another.

ThisSuitIsBlackNot pointed you to the if pragma, namely

use if $] >= 5.014, 'Foo::New';  # take advantage of latest syntax,features
use if $] <= 5.014, 'Foo::Old';  # workarounds for missing features 

to load different modules depending on the capabilities of your current perl. At run time, conditional require statements could work the same way

if ($] >= 5.014) {
    require Foo::New;
} else {
    require Foo::Old;
}

Finally, block eval is not a viable technique for version-dependent code, but string eval is. The gist is:

BEGIN {  # BEGIN block so these subs get parsed at compile time
    if ($] >= 5.014) {
        eval q^*substwrepl = sub {
            my ($str,$search) = @_;
            $str =~ s/\Q${search}\E/_/gr
        };^;
    } else {
        eval q^*substwrepl = sub {
            my ($str,$search) = @_;
            my $str_copy = $str;
            $str_copy =~ s/\Q${search}\E/_/g;
            $str_copy;
        };^;
    }
}

...  # later in your program
my $str = "Hello, world";
print "After replacement: ", substrwrepl($str, ","), "\n";
print "Original string: ", $str, "\n";

See the Test::Builder module for a real-live use of this construction.

Upvotes: 3

Dondi Michael Stroma
Dondi Michael Stroma

Reputation: 4800

Wrap it in a string-form of eval (because the block form of eval will cause the same error):

#!/usr/bin/perl

# Don't use cryptic variable names
use English;

my $str = "Hello, World!";
my $search = ',';

if ($PERL_VERSION ge v5.14) {
    print "Using version that supports /r modifier\n";

    # Perl v5.14 introduced the /r flag for regular expressions
    # From the perldoc: r  - perform non-destructive substitution and return the new value
    eval 'print "After replacement: " . ($str =~ s/\Q${search}\E/_/gr) . $/;';
    print "Original string: " . $str . $/;

} else {
    print "This version does not support the /r modifier\n";

    # Prior to Perl v5.14, no /r option existed and the original string will be clobbered
    # To deal with this, we need to make a copy first, then modify the copy instead
    my $str_copy = $str;
    $str_copy =~ s/\Q${search}\E/_/g;

    print "After replacement: " . $str_copy . $/;
    print "Original string: " . $str . $/;
}

Upvotes: 1

Related Questions