代码之家  ›  专栏  ›  技术社区  ›  Robert S. Barnes Antoni

从Perl模块导入有条件编译的函数

  •  2
  • Robert S. Barnes Antoni  · 技术社区  · 14 年前

    我有一组日志记录和调试功能,我想跨多个模块/对象使用这些功能。我希望能够使用命令行开关全局地打开/关闭它们。

    下面的代码可以做到这一点,但是,我希望能够省略包名并将所有内容保存在一个文件中。

    更具体地说,我想将日志函数名称导入每个模块,以便它们可以在没有任何包名称限定的情况下调用(类似于C++)。 use namespace; 指令),我希望能够从使用它们的脚本全局启用/禁用它们,如下面的示例代码所示。

    这与我之前提出的两个问题有关, here here

    还有一件事——我不认为我完全理解下面的代码为什么工作。

    #! /usr/bin/perl -w
    
    use strict;
    use Getopt::Long;
    
    {
        package LogFuncs;
    
        use threads;
        use Time::HiRes qw( gettimeofday );
    
        # provide tcpdump style time stamp
        sub _gf_time {
            my ( $seconds, $microseconds ) = gettimeofday();
            my @time = localtime($seconds);
    
            return sprintf( "%02d:%02d:%02d.%06ld",
                $time[2], $time[1], $time[0], $microseconds );
        }
    
        sub logerr;
    
        sub compile {
            my %params = @_;
    
            *logerr = $params{do_logging}
                ? sub {
                    my $msg = shift;
                    warn _gf_time() . " Thread " . threads->tid() . ": $msg\n";
                }
                : sub { };
        }            
    }
    
    {
        package FooObj;
    
        sub new {
            my $class = shift;
            bless {}, $class; 
        };
    
        sub foo_work { 
            my $self = shift;
            # do some foo work
            LogFuncs::logerr($self);
        }
    }
    
    {
        package BarObj;
    
        sub new {
            my $class = shift;
            my $data  = { fooObj => FooObj->new() };
            bless $data, $class;
        }
    
        sub bar_work { 
            my $self = shift;
            $self->{fooObj}->foo_work();
            LogFuncs::logerr($self);
        }
    }        
    
    my $do_logging = 0;
    
    GetOptions(
        "do_logging"    => \$do_logging,
    );
    
    LogFuncs::compile(do_logging => $do_logging);
    
    my $bar = BarObj->new();
    LogFuncs::logerr("Created $bar");
    $bar->bar_work();
    
    3 回复  |  直到 7 年前
        1
  •  6
  •   Eric Strom    14 年前

    如果希望将所有内容都保存在同一个文件中,为什么不将记录器放在文件范围内的顶部呢。

    GetOptions( do_logging => \$do_logging );
    
    my $logerr = $do_logging ? sub {logging_code_here()} : sub {};
    

    $logerr 现在可以在同一文件中该点之后定义的任何包中使用。

    my $logerr = sub { logging_code_here() };
    
    $logerr->("some string $some_var") if $do_logging;
    

    这样可以避免子例程调用,并且如果日志记录处于关闭状态,则不需要计算$logerr的字符串参数。

    您还可以设置日志记录级别:

    $logerr->("starting loop")     if $do_logging;
    
    for (@big_array) {
        $logerr->("processing $_") if $do_logging > 1;
        ...
    }
    

    编辑:虽然我不认为这是最好的做法,但根据您的评论,以下是您可能正在寻找的内容(pragma):

    use 5.010;
    use warnings;
    use strict;
    BEGIN {  # compile time
        $INC{'log.pm'}++;  # block 'require log;'
        package log;
        sub is_active {(caller 1)[10]{log}}  # test the hints hash
        sub import {
            $^H{log} = 1;  # set the hints hash for 'log'
            my $logmsg = (caller).'::logmsg';  # name of caller's sub
            no strict 'refs';
            *$logmsg = sub {print "logging: @_\n" if is_active}  # install sub
                unless *{$logmsg}{CODE}; # unless we did already
        }
        sub unimport {
            $^H{log} = 0;  # unset the hints hash
        }
    }
    
    package MyPkg;
    
    use log;
    logmsg 'hello, world!';
    
    {
      no log;
      logmsg 'nope, not here';
    }
    
    logmsg 'back again';
    
        2
  •  2
  •   kbenson    14 年前

    明确的 日志功能的包。我已经将LogFuncs::compile重命名为logerr:setup,使其更加明确,并且不会污染主名称空间。perl中的所有东西都有一个包,它只是 主要的

    至于不理解代码是如何工作的,我不知道如何解决这个问题,只是因为您没有确定您不理解的内容的任何细节。我猜你指的是包裹。Perl包可以被视为名称空间或类。对于LogFuncs包,您将其用作名称空间,在其中创建的sub将作为 静态类方法 (使用其他更传统的OO语言的一些符号,可能会对此有所启发)。包FooObj和BarObj通常通过提供构造函数作为对象使用 新的()

    #! /usr/bin/perl -w
    
    use strict;
    use Getopt::Long;
    
    use threads;
    use Time::HiRes qw( gettimeofday );
    
    # provide tcpdump style time stamp
    sub _gf_time {
    my ( $seconds, $microseconds ) = gettimeofday();
    my @time = localtime($seconds);
    
    return sprintf( "%02d:%02d:%02d.%06ld",
        $time[2], $time[1], $time[0], $microseconds );
    }
    
    sub logerr;
    
    sub logerr_setup {
        my %params = @_;
    
        *logerr = $params{do_logging}
            ? sub {
                my $msg = shift;
                warn _gf_time() . " Thread " . threads->tid() . ": $msg\n";
            }
            : sub { };
        }
    
    {
        package FooObj;
    
        sub new {
            my $class = shift;
            bless {}, $class;
        };
    
        sub foo_work {
            my $self = shift;
            # do some foo work
            main::logerr($self);
        }
    }
    
    {
        package BarObj;
    
        sub new {
            my $class = shift;
            my $data  = { fooObj => FooObj->new() };
            bless $data, $class;
        }
    
        sub bar_work {
            my $self = shift;
            $self->{fooObj}->foo_work();
            main::logerr($self);
        }
    }
    
    my $do_logging = 0;
    
    GetOptions(
        "do_logging"    => \$do_logging,
    );
    
    logerr_setup(do_logging => $do_logging);
    
    my $bar = BarObj->new();
    logerr("Created $bar");
    $bar->bar_work();
    
        3
  •  2
  •   Ether    14 年前

    如果您需要修补某些日志记录方法(或者在开发人员环境与生产环境中有条件地执行其他操作),可以将代码放入 import() 方法,并在 use 要启用/禁用它的行:

    # this is in MyLogger.pm:
    package MyLogger;
    
    use strict; use warnings;
    use Exporter;
    
    our @EXPORT_OK = qw(logerr);
    
    sub import
    {
         my ($class, $debug) = @_;
    
         if ($debug)
         {
              # do whatever you need here - patch methods, turn up the log level to "everything", etc
    
              # imports logerr method
              MyLogger->export_to_level(1, 'logger');
         }
    }
    
    sub logerr
    {
        # your patched method here..
    }
    
    1;
    
    # (now in your application code...)
    use MyLogger "debug";    # enables debugging mode
    # or:
    use MyLogger MyConfig::DEBUG;    # uses a debugging constant that you defined elsewhere