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

#!/usr/local/bin/perl -w =for request for assistance im trying to work out an auto-cat feature for Log::Log4perl, which needs greater efficiency of caller() (or 1 time use of it;) =head SYNOPSIS use Log::Log4perl::AutoCategorize ( alias_as => 'Logger' ); # want this Logger->debug() # to mean something like Logger->debug_<$callerpkg,$caller_sub,$caller_ln>(@_) =head1 A has autoload that munges a name, and installs a hello-from method into the symbol table. user program must create the name before invoking it, so that =cut use strict; no strict 'refs'; package A; use Data::Dumper; use vars qw($AUTOLOAD); use Carp 'cluck'; sub AUTOLOAD { # compile and goto& munged method-name # (my $meth = $AUTOLOAD) =~ s/.*:://; return if $meth eq 'DESTROY'; my ($package, $filename, $line, $subroutine) = caller(0); my $buf = "<$meth>: $package, $filename, $line, $subroutine\n"; $buf =~ s|[\./]||g; print "creating: $buf"; *{'A::'.$meth} = sub { print "invoked: $buf"; # force string interpolation, no closure (d +ont need) # print "\twith ", Dumper (\@_); }; goto &{'A::'.$meth}; } package main; A::auto("string{}"); cooperative_dynamic_name_invoke(); print "done\n"; sub cooperative_dynamic_name_invoke { # macro-like invoke of auto-names # invoker help needed; by creating custom sub-name # for each lexical invocation foreach (1..2) { # autoload compiles sub on 1st iteration # (and vivifies name in symbol table) # 2nd time, new asub called directly my $munge = "dyn_name_000"; # wo reinit, would just make 3,4 $munge++; # this doesnt give string++, but irrelevant t +o test A->$munge("1st try"); $munge++; A->$munge("2nd try\n"); $munge++; A->$munge("3rd try\n"); } } __END__ =head1 for HUNCH munging name (for me, here) has 2 steps: 1. producing munged name from user context. (he called me from X, he knows why) includes - by def - adding that function to symbol table 2. causing that new name to be called from that spot in code (job for optimize ??) I dont understand; 1. how to recognize (particularly at compile time) whether AUTOLOAD would eventually be called, or how to get the lexical scope during compilation. a compile-time analogue to caller() would be cool. 2. when to 'require optimizer extend ....' whats it mean to do so in INIT{} or CHECK {} blocks ? My hope is that porters with optimizer.pm-fu will find this to be a killer-app with sufficient merit for suggestions or solutions ;-) (clever use of) would address is how to patch the opcode that invokes the referent-function. =cut use optimizer extend => sub { print "dump ", Dumper \@_ if 0 #or $_[0]->name() eq "goto" #or ref $_[0] =~ /CV/i ; # =~ /__ANON__/; }; sub loop { foreach (1..2) { # if hashref were CONSTANT, and not rebuilt for each invocation, # it could preserve the A::fancy({arb=>1},1); A::fancy({ok=>2},2); A::auto({}); A::auto({}); } use Data::Dumper; print Dumper (\%A::); } __END__

Replies are listed 'Best First'.
Re: AUTOMUNGE revisited - revised
by jimc (Sexton) on Jan 22, 2003 at 05:41 UTC
    #!/usr/local/bin/perl -w use strict; no strict 'refs'; use vars qw( $opt_m $opt_M ); package A; use Data::Dumper; use vars qw($AUTOLOAD); my %cache; use Carp 'cluck'; use optimizer extend => sub { my $name = $_[0]->name(); cluck "dump ", Dumper \@_ if 0 or $name eq "goto" # or $name eq "print" or ref $_[0] =~ /CV/i ; # =~ /__ANON__/; }; sub AUTOLOAD { # Use an extension of the default optimizer (my $meth = $AUTOLOAD) =~ s/.*:://; return if $meth eq 'DESTROY'; my ($package, $filename, $line, $subroutine) = caller(0); my $cat = join '_', $meth, $package, $filename, $line; $cat =~ s|[\./:]||g; my $code; unless (exists $cache{$cat}) { $code = sub { shift @_; print " calling <$meth> ",$cat," (@_)\n" } +; print "creating <$meth> $cat\n"; $cache{$cat} = $code; *{'A::'.$cat} = $code if $main::opt_m; # vivify specific-usage nam +e *{'A::'.$meth} = $code unless $main::opt_M; # vivify generic name } else { # runs when munging $code = $cache{$cat}; print "fetching: $cat\n"; } goto &$code; } package main; use Getopt::Std; getopts('mM') or die <<EOH; -m : enables munged sub-name vivify -M : suppresses non-munged sub-name vivify, causes fetch: -M forces AUTOLOAD to run on 2nd pass, by not vivifying base name EOH #A::auto("string"); loop(); print "done\n"; sub loop { my $munge; foreach (1..2) { print "$_:\n"; A->debug('aval'); A->info('another'); A->info('now that info() is built, the line numbers wrong.'); A->info('more importantly, this call needs its own handler, not th +e other one.'); A->something('anything'); A->other('anything'); } } __END__ =head1 extending Log::Log4perl package Log::Log4perl::AutoCategorize # notional extension Log::Log4perl provides a flexible logging system which allows configuration of one or more output streams, each collecting messages from a set of logging-categories / sources. Id like to extend it by coupling the logging-category to caller() info, following benefits accrue; 1. END{} handler can report on Logger coverage. 2. scope friendly filtering, 3. well, 2 is something.. =head1 my code Im using AUTOLOAD to implement A->$invoke(), for all values of $invoke; Ill eventually delegate them to Log::Log4perl->debug(), info(), warn(), or error(), dependent upon config. AUTOLOAD uses caller() to make and test the logging-category, and woul +d either write a log entry, or just return. That yes/no decision can + result in a noop, or an appropriate print to stream. method-munging guarantees that each $invoke() is lexically unique (fil +e & line, approx), and carries context for purposes of logging and fi +ltering. But once name is munged, its not accessible by original cal +ling point, and AUTOLOAD must regenerate the munged-name to look up t +he cached coderef. =head1 SYNOPSIS - Desired # assuming a package like this use Log::Log4perl::AutoCategorize ( alias_as => 'Logger' ); sub doit { ... # want simple invocations, like this Logger->debug(@_); # to really mean Logger->debug_<$callerpkg,$caller_sub,$caller_ln> (@_); # its really a macro }
    =head1 what I need

    caller(), Log4perl::get_logger(), Log4perl::is_writable(), are quite expensive for repeated use in a system logger.

    if I can use optimizer within AUTOLOAD to patch each invocation, I can build sub{}s or link to previously built ones, and eliminate subequent calls to AUTOLOAD. I also get to grab everything I need to provide a customized response for the caller.

    aside: the contrib of the munged-name methods to the size of the symbol table is linear with the config size, not the count of invocations.

    I hope this is an interesting enough application of B::Generate, optimizer modules to attract your attention / curiosity / spare clues. Thanks in advance.