在Perl中编写宏

问题描述 投票:13回答:4
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";
perl macros
4个回答
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。欧文斯建议使用它。


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用户。


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有一个使用Vim的method。我使用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版本上。


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关键字。

© www.soinside.com 2019 - 2024. All rights reserved.