从 Perl XS 扩展打印到标准输出
我最近开始尝试使用 XS 编写 Perl (v5.8.8) 扩展。我正在编写的方法之一收集一堆数据并将其发送给客户端。我想编写一些单元测试来对输出进行断言,但我遇到了一个问题:PerlIO 方法似乎没有通过与 print 相同的通道传递数据在 Perl 中调用确实如此。通常,您可以连接到 STDOUT 文件处理程序并拦截结果,但 PerlIO 方法似乎完全绕过了这一点。
我在下面粘贴了一个示例,但我的测试的基本要点是:Tie
in to STDOUT
,运行代码,untie
,返回收集的字符串。这样做,我能够捕获 print
语句,但不能捕获来自模块的 PerlIO_*
调用。我尝试过使用 PerlIO_write
、PerlIO_puts
、PerlIO_printf
等。没有骰子。
从头开始,这是我正在做的事情的最小重现:
h2xs -A -n IOTest
cd IOTest
将其放入 IOTest.xs
中:
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
MODULE = IOTest PACKAGE = IOTest
void
oink ()
CODE:
PerlIO_puts(PerlIO_stdout(), "oink!\n");
这将进入一个名为 test.pl
的文件(有趣的部分在底部附近,其他所有内容都只是为了捕获标准输出):
# Set up the include path to match the build directories
BEGIN {
push @INC, './blib/lib/';
push @INC, './blib/arch/auto/IOTest';
}
use IOTest;
# This package is just a set of hooks for tieing in to stdout
{
# Lifted from the Test::Output module found here:
# http://search.cpan.org/~bdfoy/Test-Output-1.01/lib/Test/Output.pm
package OutputTie;
sub TIEHANDLE {
my $class = shift;
my $scalar = '';
my $obj = shift || \$scalar;
bless( $obj, $class);
}
sub PRINT {
my $self = shift;
$$self .= join(defined $, ? $, : '', @_);
$$self .= defined $\ ? $\ : '';
}
sub PRINTF {
my $self = shift;
my $fmt = shift;
$$self .= sprintf $fmt, @_;
}
sub read {
my $self = shift;
my $data = $$self;
$$self = '';
return $data;
}
}
# Runs a sub, intercepts stdout and returns it as a string
sub getStdOut (&) {
my $callback = shift;
select( ( select(STDOUT), $| = 1 )[0] );
my $out = tie *STDOUT, 'OutputTie';
$callback->();
my $stdout = $out->read;
undef $out;
untie *STDOUT;
return $stdout;
}
# This is the interesting part, the actual test:
print "Pre-capture\n";
my $output = getStdOut(sub {
print "before";
IOTest::oink();
print "after";
});
print "Captured StdOut:\n" . $output . "\nend\n";
构建和测试只是一个问题:
perl Makefile.PL
make
perl test.pl
我看到的输出是:
Pre-capture
oink!
Captured StdOut:
beforeafter
end
显然,我期待“oink!”夹在“之前”和“之后”之间,但这似乎并没有发生。
有什么想法吗?
I recently started playing around with writing Perl (v5.8.8) extensions using XS. One of the methods I am writing collects a bunch of data and splats it to the client. I want to write some unit tests that make assertions against the output, but I'm running in to a problem: It doesn't appear that the PerlIO methods are passing data through the same channels as a print
call in Perl does. Normally, you can tie in to the STDOUT file handler and intercept the result, but the PerlIO methods seem to be bypassing this completely.
I've pasted an example below, but the basic jist of my test is this: Tie
in to STDOUT
, run code, untie
, return collected string. Doing this, I'm able to capture print
statements, but not the PerlIO_*
calls from my module. I've tried using PerlIO_write
, PerlIO_puts
, PerlIO_printf
, and more. No dice.
From scratch, here is a minimal repro of what I'm doing:
h2xs -A -n IOTest
cd IOTest
Put this in to IOTest.xs
:
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
MODULE = IOTest PACKAGE = IOTest
void
oink ()
CODE:
PerlIO_puts(PerlIO_stdout(), "oink!\n");
And this goes in to a file called test.pl
(The interesting part is near the bottom, everything else is just for capturing stdout):
# Set up the include path to match the build directories
BEGIN {
push @INC, './blib/lib/';
push @INC, './blib/arch/auto/IOTest';
}
use IOTest;
# This package is just a set of hooks for tieing in to stdout
{
# Lifted from the Test::Output module found here:
# http://search.cpan.org/~bdfoy/Test-Output-1.01/lib/Test/Output.pm
package OutputTie;
sub TIEHANDLE {
my $class = shift;
my $scalar = '';
my $obj = shift || \$scalar;
bless( $obj, $class);
}
sub PRINT {
my $self = shift;
$self .= join(defined $, ? $, : '', @_);
$self .= defined $\ ? $\ : '';
}
sub PRINTF {
my $self = shift;
my $fmt = shift;
$self .= sprintf $fmt, @_;
}
sub read {
my $self = shift;
my $data = $self;
$self = '';
return $data;
}
}
# Runs a sub, intercepts stdout and returns it as a string
sub getStdOut (&) {
my $callback = shift;
select( ( select(STDOUT), $| = 1 )[0] );
my $out = tie *STDOUT, 'OutputTie';
$callback->();
my $stdout = $out->read;
undef $out;
untie *STDOUT;
return $stdout;
}
# This is the interesting part, the actual test:
print "Pre-capture\n";
my $output = getStdOut(sub {
print "before";
IOTest::oink();
print "after";
});
print "Captured StdOut:\n" . $output . "\nend\n";
Building and testing is then just a matter of:
perl Makefile.PL
make
perl test.pl
The output I'm seeing is:
Pre-capture
oink!
Captured StdOut:
beforeafter
end
Obviously, I'm expecting "oink!" to be sandwiched between "before" and "after", but that doesn't appear to be happening.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为捕捉有问题。比较:
I think the capturing is faulty. Compare: