So I've been toying around with wrapping some C functions that simply take arguments and print via stdout. The input was fairly straightforward. Capturing the output however, has been difficult to say the least.
Regardless of the PerlIO layer, I can't seem to capture data written to FILE streams from XS.
#!/usr/bin/perl use strict; use warnings; use Inline C => <<'C_HUNK'; void c_output() { fprintf(stdout, "###c_output"); } void c_output_newline() { fprintf(stdout, "###c_output_newline\n"); } C_HUNK sub test_global { my $out_var = ''; my $prev_select = select; open my $prev_stdout, '>&', \*STDOUT or die "Error: $!"; close STDOUT; open STDOUT, '>', \$out_var or die "Error: $!"; select $out_var; $|=1; select $prev_select; outp_test(); open STDOUT, '>&', \$prev_stdout or die "Error: $!"; return $out_var; } sub outp_test { # Test with newline (autoflush) warn "\tprinting [perl_output_newline]\n"; print STDOUT "***perl_output_newline\n"; warn "\tcalling [c_output_newline]\n"; c_output_newline(); # Test without newline warn "\tprinting [perl_output]\n"; print STDOUT "***perl_output"; warn "\tcalling [c_output]\n"; c_output(); } warn "===============dry run============\n"; outp_test(); print "\n"; warn "=============test_global==========\n"; my $buffer = test_global(); $buffer =~ s/\n/\\n/g; warn "test_global buffer:\n($buffer)\n"; print "\n";
When I set PERLIO to perlio (ala default), I get the following output:
===============dry run============ printing [perl_output_newline] ***perl_output_newline calling [c_output_newline] ###c_output_newline printing [perl_output] calling [c_output] ***perl_output###c_output =============test_global========== printing [perl_output_newline] calling [c_output_newline] printing [perl_output] calling [c_output] test_global buffer: (***perl_output_newline\n***perl_output)
The dry run is logically outputting the non-flushed Perl and C output as soon as there is a flush. The redirection however, never returns any of the C output.
Setting PERLIO to stdio gives the following:
===============dry run============ printing [perl_output_newline] ***perl_output_newline calling [c_output_newline] ###c_output_newline printing [perl_output] calling [c_output] ***perl_output =============test_global========== printing [perl_output_newline] calling [c_output_newline] printing [perl_output] calling [c_output] test_global buffer: (***perl_output_newline\n***perl_output) ###c_output
The dry run only outputs the flushed C output, and not the non-flushed. The redirection seems to be caching the non-flushed C output, and releasing it only after everything else is done.
So... I'm hoping to avoid forking, but I'm thinking I might not have any other option. Oh, tested this on 5.10.1 and 5.8.8 with the same behavior.
Any ideas?
In reply to Redirecting XS stdout by innominate
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |