如何在Perl中实现断言?

U. *_*ndl 7 perl assert eval

当试图assert()在Perl中实现C的宏时,存在一些基本问题.首先考虑以下代码:

sub assert($$) {
   my ($assertion, $failure_msg) = @_;
   die $failure_msg unless $assertion;
}

# ...
assert($boolean, $message);
Run Code Online (Sandbox Code Playgroud)

虽然这有效,但它不像C:在C中我会写assert($foo <= $bar),但有了这个实现,我必须写assert($foo <= $bar, '$foo <= $bar'),即重复条件为字符串.

现在我想知道如何有效地实现这一点.easy变量似乎将字符串传递给assert()并用于eval计算字符串,但在评估eval时无法访问变量.即使它可以工作,每次解析和评估条件也会非常低效.

传递表达式时,我不知道如何从中创建一个字符串,特别是在它已经被评估时.

另一个使用assert(sub { $condition })代码ref更容易制作字符串的变体被认为太难看了.

构造assert(sub { (eval $_[0], $_[0]) }->("condition"));

sub assert($)
{
    die "Assertion failed: $_[1]\n" unless $_[0];
}
Run Code Online (Sandbox Code Playgroud)

会怎么做,但打电话很难看.我正在寻找的解决方案编写条件检查一次,同时能够重现原始(未评估)条件有效地评估条件.

那么更优雅的解决方案是什么?显然,如果Perl有一个宏或类似的语法机制允许在编译或评估之前转换输入,那么解决方案会更容易.

Ste*_*ker 8

使用B :: Deparse

#!/usr/bin/perl
use strict;
use warnings;

use B::Deparse;
my $deparser = B::Deparse->new();

sub assert(&) {
    my($condfunc) = @_;
    my @caller    = caller();
    unless ($condfunc->()) {
        my $src = $deparser->coderef2text($condfunc);
        $src =~ s/^\s*use\s.*$//mg;
        $src =~ s/^\s+(.+?)/$1/mg;
        $src =~ s/(.+?)\s+$/$1/mg;
        $src =~ s/[\r\n]+/ /mg;
        $src =~ s/^\{\s*(.+?)\s*\}$/$1/g;
        $src =~ s/;$//mg;
        die "Assertion failed: $src at $caller[1] line $caller[2].\n";
    }
}

my $var;
assert { 1 };
#assert { 0 };
assert { defined($var) };

exit 0;
Run Code Online (Sandbox Code Playgroud)

测试输出:

$ perl dummy.pl
Assertion failed: defined $var at dummy.pl line 26.
Run Code Online (Sandbox Code Playgroud)


tob*_*ink 7

CPAN上有一大堆断言模块.这些都是开源的,所以很容易看到它们,看看它们是如何完成的.

Carp :: Assert是一种低魔法的实现.它在其文档中链接到一些更复杂的断言模块,其中一个是我的模块PerlX :: Assert.


mob*_*mob 5

使用caller并提取做出断言的源代码行?

sub assert {
    my ($condition, $msg) = @_;
    return if $condition;
    if (!$msg) {
        my ($pkg, $file, $line) = caller(0);
        open my $fh, "<", $file;
        my @lines = <$fh>;
        close $fh;
        $msg = "$file:$line: " . $lines[$line - 1];
    }
    die "Assertion failed: $msg";
}

assert(2 + 2 == 5);
Run Code Online (Sandbox Code Playgroud)

输出:

Assertion failed:  assert.pl:14: assert(2 + 2 == 5);
Run Code Online (Sandbox Code Playgroud)

如果您使用Carp::croak而不是die,则Perl还将报告堆栈跟踪信息,并确定调用失败的断言的位置。