在Perl中编写宏

时间:2010-09-07 17:39:27

标签: perl macros

open $FP, '>', $outfile or die $outfile." Cannot open file for writing\n";
  • 我的代码中有很多次这样的陈述。

  • 我想保持所有这些语句的格式相同,这样当某些内容发生变化时,它只会在一个地方更改。

  • 在Perl中,我该如何解决这种情况?

  • 我应该使用宏还是函数?

我已经看过这个SO线程How can I use macros in Perl?,但它没有说明如何编写像

这样的通用宏
#define fw(FP, outfile) open $FP, '>', \
        $outfile or die $outfile." Cannot open file for writing\n";

4 个答案:

答案 0 :(得分:25)

首先,您应该将其写为:

open my $FP, '>', $outfile or die "Could not open '$outfile' for writing:$!";

包括open失败的原因。

如果要封装它,可以写:

use Carp;

sub openex {
    my ($mode, $filename) = @_; 
    open my $h, $mode, $filename
        or croak "Could not open '$filename': $!";
    return $h;
}

# later

my $FP = openex('>', $outfile);

从Perl 5.10.1开始,autodie是核心,我将第二个Chas。欧文斯建议使用它。

答案 1 :(得分:10)

Perl 5真的没有宏(有源过滤器,但它们很危险,很难看,所以很丑,即使我不会把你链接到文档)。函数可能是正确的选择,但您会发现它使新用户更难以阅读您的代码。更好的选择可能是使用autodie编译指示(它是Perl 5.10.1的核心)并且只删除了or die部分。

如果您使用Vim,另一个选项是使用snipMate。您只需输入fw<tab>FP<tab>outfile<tab>即可生成

open my $FP, '>', $outfile
    or die "Couldn't open $outfile for writing: $!\n";

snipMate文字是

snippet fw
    open my $${1:filehandle}, ">", $${2:filename variable}
        or die "Couldn't open $$2 for writing: $!\n";

    ${3}

我相信其他编辑有类似的功能,但我是Vim用户。

答案 2 :(得分:7)

在Perl中有几种方法可以处理类似于C宏的东西:源过滤器,子例程,Template :: Toolkit,或者在文本编辑器中使用功能。

源过滤器

如果你有一个C / CPP样式的预处理器宏,可以使用预编译source filter在Perl(或实际上,任何语言)中编写一个。您可以编写相当简单的复杂Perl类,这些类对源代码的文本进行操作,并在代码转到Perl编译器之前对其执行转换。您甚至可以直接通过CPP预处理器运行Perl代码,以获得使用Filter::CPP在C / CPP中获得的宏扩展的确切类型。

Damian Conway的Filter::Simple是Perl核心发行版的一部分。使用Filter :: Simple,您可以轻松编写一个简单的模块来执行您描述的宏。一个例子:

package myopinion;
# save in your Perl's @INC path as "myopinion.pm"...
use Filter::Simple;
FILTER { 
    s/Hogs/Pigs/g; 
    s/Hawgs/Hogs/g;
    }
1; 

然后是Perl文件:

use myopinion;
print join(' ',"Hogs", 'Hogs', qq/Hawgs/, q/Hogs/, "\n");
print "In my opinion, Hogs are Hogs\n\n";

输出:

Pigs Pigs Hogs Pigs 
In my opinion, Pigs are Pigs

如果您重写了FILTER以替换所需的宏,则Filter :: Simple应该可以正常工作。 Filter :: Simple可以限制为代码的一部分来制作变电站,例如可执行部分而不是POD部分;只在字符串中;只在代码中。

根据我的经验,源滤镜并未广泛使用。我大多看到他们蹩脚的尝试加密Perl源代码或幽默的Perl obfuscators。换句话说,我知道它可以通过这种方式完成,但我个人对他们的推荐不够或者说不要使用它们。

子程序

SinanÜnüropenex subroutine是实现这一目标的好方法。我只会补充一点,你会看到一个常见的旧习惯用法是将一个引用传递给类似于这样的类型:

sub opensesame {
   my $fn=shift;
   local *FH;
   return open(FH,$fn) ? *FH : undef;
}

$fh=opensesame('> /tmp/file');

请阅读perldata了解原因为何......

模板工具包

Template::Toolkit可用于处理Perl源代码。例如,您可以按以下方式编写模板:

[% fw(fp, outfile) %] 

通过Template :: Toolkit运行它可以导致扩展和替换为:

open my $FP, '>', $outfile or die "$outfile could not be opened for writing:$!"; 

Template :: Toolkit最常用于将凌乱的HTML和其他演示代码与Web应用程序中的应用程序代码分开。 Template :: Toolkit非常积极地开发并且有很好的文档记录。如果您的唯一用途是您建议的类型的宏,则可能是过度杀伤。

文字编辑

查斯。 Owens有method使用Vim。我使用BBEdit并且可以轻松编写文本工厂来替换我想要使用的精确和不断变化的开放的开放框架。或者,您可以在“Perl”文件夹的“Resources”目录中放置一个完成模板。按下您定义的一系列键时,将使用这些完成骨架。几乎任何严肃的编辑器都具有类似的功能。

使用BBEdit,您甚至可以在文本替换逻辑中使用Perl代码。我用这种方式Perl::Critic。您可以在BBEdit中使用Template::Toolkit来处理具有一定智能的宏。它可以设置,因此在输出要测试或编译的版本之前,模板不会更改源代码;编辑器基本上充当预处理器。

使用文本编辑器的两个潜在问题。首先是单向/一次变换。如果你想改变你的“宏”所做的事情,你就不能这样做,因为之前的“宏”文本已经被使用了。您必须手动更改它们。第二个潜在的问题是,如果您使用模板表单,则无法将源代码的宏版本发送给其他人,因为正在编辑器中执行预处理。

不要这样做!

如果您键入perl -h以获取有效的命令切换,您可能会看到一个选项:

-P                run program through C preprocessor before compilation

诱人!是的,您可以通过C预处理器运行Perl代码并展开C样式宏并使用#defines。放下那把枪;走开;不要这样做。存在许多平台不兼容性和语言不兼容性。

你遇到这样的问题:

#!/usr/bin/perl -P
#define BIG small
print "BIG\n";
print qq(BIG\n);

打印:

BIG
small

在Perl 5.12中,-P开关已被移除......

<强>结论

这里最灵活的解决方案就是编写一个子程序。您的所有代码都在子例程中可见,可以轻松更改,并且调用时间更短。除了代码的可读性之外,没有真正的缺点。

Template :: Toolkit被广泛使用。您可以编写复杂的替换,其行为类似于宏,甚至比C宏更复杂。如果您对宏的需求值得学习,请使用Template :: Toolkit。

对于非常简单的情况,请在编辑器中使用单向转换。

如果您真的想要C风格的宏,可以使用Filter::CPP。这可能与perl -P开关具有相同的不兼容性。我不能推荐这个;只是学习Perl方式。

如果你想在编译之前对你的代码运行Perl一个衬里​​和Perl正则表达式,请使用Filter :: Simple。

不要使用-P开关。无论如何,你无法使用更新版本的Perl。

答案 3 :(得分:0)

对于像open这样的东西,我认为在你的分解程序中包含close非常有用。这是一种看起来有点奇怪的方法,但是封装了一个典型的开/关习惯用法。

sub with_file_do(&$$) {
  my ($code, $mode, $file) = @_;
  open my $fp, '>', $file or die "Could not open '$file' for writing:$!";
  local $FP = $fp;
  $code->(); # perhaps wrap in an eval
  close $fp;
}

# usage
with_file_do {
  print $FP "whatever\n";
  # other output things with $FP
} '>', $outfile;

最后指定的开放参数有点奇怪,但它允许您避免必须指定 sub 关键字。