home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Perl CookbookPerl CookbookSearch this book

12.13. Overriding a Built-in Function in All Packages

12.13.1. Problem

You want to change the definition of a core built-in function within your entire program, not just the current package.

12.13.2. Solution

Manually import, via direct symbol-table manipulation, the function into the CORE::GLOBAL pseudopackage.

*CORE::GLOBAL::int = \&myown_int;

12.13.3. Discussion

The technique demonstrated in the previous recipe only overrides a built-in in a particular package. It doesn't change everything for your whole program, no matter what package that function is called from. To do so would risk changing the behavior of code from modules you didn't write, and which were therefore not prepared for the change.

It has been said that Unix was not designed to stop you from doing stupid things, because that would also stop you from doing clever things. So, too, with Perl. Just because overriding a function in all packages at once might seem, well, imprudent doesn't mean a clever person won't someday find a marvelous use for such a facility.

For example, let's suppose that you've decided that the core int function's behavior of integer truncation, also known as rounding toward zero, is so annoying to your program that you want to provide an alternative by the same name. This would do it:

package Math::Rounding;
use warnings;
use Carp;
use Exporter;
our @EXPORT = qw(int);
our @ISA    = qw(Exporter);

sub int(;$) {
    my $arg = @_ ? shift : $_;
    use warnings FATAL => "numeric";  # promote to die( )ing
    my $result = eval { sprintf("%.0f", $arg) };
    if ($@) {
        die if $@ !~ /isn't numeric/;
        $@ =~ s/ in sprintf.*/ in replacement int/s;
        croak $@;
    } else {
        return $result;
    } 
}

Your replacement version uses sprintf( ) to round to the closest integer. It also raises an exception if passed a non-numeric string. A program could access this function either by saying:

use Math::Rounding ( );
$y = Math::Rounding::int($x);

or by importing the function and overriding the built-in:

use Math::Rounding qw(int);
$y = int($x);

However, that only manages to replace the built-in for the current package. To replace it in all packages, at some point during compile time you'll have to execute a line of code like this:

*CORE::GLOBAL::int = \&Math::Rounding::int;

The standard File::Glob module allows you to change Perl's core glob operator using special import tags:

## override the core glob, forcing case sensitivity
use File::Glob qw(:globally :case);
my @sources = <*.{c,h,y}>

## override the core glob forcing case insensitivity
use File::Glob qw(:globally :nocase);
my @sources = <*.{c,h,y}>

The module does this with its own version of import that detects those tags and makes the necessary assignments. You could do this, too. That way, this:

use Math::Rounding qw(-global int);

would make Perl use your replacement version for all calls to int from any package anywhere in your program. Here's a replacement import function that handles this:

sub import {
    if (@_ && $_[1] =~ /^-/) {
        if ($_[1] ne "-global") { 
            croak "unknown import pragma";
        }
        splice(@_, 1, 1);              # discard "-global"
        no warnings "once";     # suppress "used only once" warnings
        *CORE::GLOBAL::int = \&int;  
    } else {
        die;
    } 
    _ _PACKAGE_ _ -> export_to_level(1, @_);
}

The assignment happens only if the first thing to import is "-global". The last line in our import function uses part of the Exporter module's internal API to handle any normal import.

12.13.4. See Also

Recipe 12.12; the section on "Overriding Built-in Functions" in Chapter 11 of Programming Perl and in perlsub(1); the documentation for the standard BSD::Glob module, as well as its source code



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.