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

Greetings Monks!

I'm a little new to perl, so bear with me if my lingo is wrong. :)

I'm working with a large legacy code base and looking for a way to be able to "generate" new functions implicitly and dispatch them to existing functions via an algorithm at runtime. It's akin to an AOP thing.

For example, I might want to take a function like:

$value = Foo::bar($x);
... and provide a "logged" version of that call in the form:
$value = Logged::Foo::bar("/tmp/log", $x);
... without writing the wrapper for every single function.

Originally I had a version that worked using AUTOLOAD. This was almost EXACTLY what I wanted, except that I couldn't figure out how to get one AUTOLOAD function to dispatch for multiple packages.

Enter: UNIVERSAL::AUTOLOAD. :)

So UNIVERSAL::AUTOLOAD works like a charm except that I get a warning that using this to dispatch non-methods is deprecated (perl 5.8.3) and that the workaround is to, you guessed it, explicitly define the AUTOLOAD in each package that does any dispatching.

Is there another way to do this? To summarize, I need to be able to write a single function that's deferred to as the "last resort" for name-based dispatch so that I can shim in code that can rewrite the requests in order to augment them and dispatch them to other functions.

I'm looking forward to working this out to provide some generalized predicate-dispatch type notions in some later projects, but I don't want to go too far down an unsupported path if I can possibly find a supported one.

Thanks in advance!
zonk

Replies are listed 'Best First'.
Re: Dynamic function dispatching...
by merlyn (Sage) on May 04, 2005 at 16:47 UTC
      Hi Randal,

      Thanks for the quick reply!

      The short answer is that it might. :)

      After glancing at the perldocs for Aspect the only couple of questions I'll need to answer are:

      1. Can I advise functions that don't exist?
      2. Can I write "around" advice that can call another function than the one it appears that I want to call? (e.g. Can advice to Logger::Foo::bar() eventually call Foo::bar() with modified args and never actually try to dispatch to Logger::Foo::bar()?)

      I'll start digging into Aspect now to find these answers. Maybe this will be a winner. :)

      Thanks again!
      zonk

Re: Dynamic function dispatching...
by bageler (Hermit) on May 04, 2005 at 20:45 UTC
    the Memoize module creates caching for arbitrary functions. You may be able to modify it to be a Logger package that automatically rewraps your functions to provide logging.
Re: Dynamic function dispatching...
by shemp (Deacon) on May 04, 2005 at 23:55 UTC
    Based on the responses already in this thread, it sounds like you're abandoning AUTOLOAD for this project. Thats probably a good idea. Heavy use of AUTOLOAD leads to some serious ugliness, particularly when inheritance / polymorphism is involved.

    2 threads i started a while ago have helped me a lot, and may be able to offer you some insight into autoload problems. So if you get a chance, check out these:

    AUTOLOAD - the good, the bad, and the ugly
    AUTOLOAD cascade

Re: Dynamic function dispatching...
by Animator (Hermit) on May 04, 2005 at 17:28 UTC

    Can't you call your functions/subs as methods?

    That is, Logged::Foo->bar(...) instead of Logged::Foo::bar(...), this will make the warning go away since you are calling a method, and not a function. (which is what the warning is about)

Re: Dynamic function dispatching...
by DrHyde (Prior) on May 06, 2005 at 10:26 UTC
    If you want a generic logger for any function, I suggest writing a logging function which takes as parameters a reference to the function to be logged, and the stuff to pass to it. If you're not familiar with function references, think of a C pointer to a function and you'll get the right idea.

    Be careful to call the referred to function in the right context - list or scalar or void (see wantarray). You might also want to play games with caller like TheDamian does in his evil Hook::LexWrap.