tachyon has asked for the wisdom of the Perl Monks concerning the following question:

I am familiar enough with XS to be able to do most basic things, but I have a problem and don't know the best approach. Essentially it is quite simple. I have some stream based C widgets that take input on STDIN and spew output on STDOUT. Typically these are compiled into little command line widgets so you might do:

widget1 --flag < input_file | widget2 | widget3 > output_file

As you might expect these are pluggable components of a larger system and I was too lazy to write buffering IO. Now I want to access these widgets via perl. Emulating the pipe linking is of course easily handled in C, but what I want to do is be able to pass two open filehandles to my XS code which will take the place of the input_file and output_file streams.

#!/usr/bin/perl open $in_fh, "<$in" or die $!; open $out_fh, ">$out" or die $!; XS_widget( $in_fh, $out_fh ); # reads stuff from $in_fh and writes s +tuff to $out_fh

I presume/hope that there must be some sort of macro that lets me exract the file descriptor out of an SV. I have done some searching and found stuff like SVt_PVGV Glob (possible a file handle) but my attempts to make anything work have failed. 'file' is not a good search term. It seems like it sould be as easy as extract the file pointer, cast it to FILE and away you go but.....

I have tried the R&D method with XS modules I know that take Perl filehandles like HTML::Parser but have found they do the read in perl and pass buffered chunks to the C. Any pointers would be appreciated.

While it would appear simple to just pass the filenames to the XS and let the open happen in C I do actually want to use Perl streams.

cheers

tachyon

Replies are listed 'Best First'.
Re: Linking Open Perl Filehandles to Stream Based C code via XS
by dave_the_m (Monsignor) on Nov 06, 2004 at 12:45 UTC
    Well, the core code for the fileno function looks like the following (which also handles the case of tied filehandles). It's basically PerlIO_fileno(IoIFP(GvIO((GV*)sv))) that you're after.
    PP(pp_fileno) { dSP; dTARGET; GV *gv; IO *io; PerlIO *fp; MAGIC *mg; if (MAXARG < 1) RETPUSHUNDEF; gv = (GV*)POPs; if (gv && (io = GvIO(gv)) && (mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar))) { PUSHMARK(SP); XPUSHs(SvTIED_obj((SV*)io, mg)); PUTBACK; ENTER; call_method("FILENO", G_SCALAR); LEAVE; SPAGAIN; RETURN; } if (!gv || !(io = GvIO(gv)) || !(fp = IoIFP(io))) { /* Can't do this because people seem to do things like defined(fileno($foo)) to check whether $foo is a valid fh. if (ckWARN2(WARN_UNOPENED,WARN_CLOSED)) report_evil_fh(gv, io, PL_op->op_type); */ RETPUSHUNDEF; } PUSHi(PerlIO_fileno(fp)); RETURN; }

    Dave.

Re: Linking Open Perl Filehandles to Stream Based C code via XS
by beauregard (Monk) on Nov 06, 2004 at 20:02 UTC
    I've had luck with something along the lines of:
    XS(XS_widget) { dXSARGS; FILE* in = PerlIO_findFILE(IoIFP(sv_2io(ST(0)))); FILE* out = PerlIO_findFILE(IoIFP(sv_2io(ST(1)))); /* copy in to out */ }
    Obviously you'd want to do stuff like error handling and arg counts and stuff, but that's basically how xsubpp handles XSUB definitions like the following in .xs files:
    char* read(fd,count) FILE* fd int count CODE: ...
    c.

      Thanks very much, that's just what I was after.

      cheers

      tachyon