Steve Hay <[EMAIL PROTECTED]> writes:
>Hi,
>
>I'm new to XS and was wondering what the best strategy is for opening a
>file in C as a file stream (FILE *) and returning that to Perl as a
>filehandle.

The best strategy is not to do that unless you really have to ;-)

I assume the _real_ XS has to wrap a library which returns a FILE * ?
If so does that library have an alternate interface (say for reading 
from in-memory)? 
If you are writing the library it would be better to open file perl-side
and pass handle in, or use PerlIO * abstraction.

In devel track perl has its own IO system and using FILE * limits
what you can do. On the release track (as you have discovered) the 
typemap entries for FILE * are a bit of a mess.

>
>I wrote a simple module called Foo in which Foo.xs defines the following
>function, a simple wrapper to C's fopen():
>
>FILE *
>foo(file, mode)
>        char *    file;
>        char *    mode;
>    CODE:
>        RETVAL = fopen(file, mode);
>    OUTPUT:
>        RETVAL
>
>If I now call Foo::foo() in the Perl as follows:
>
>    my $fh = Foo::foo("test.txt", "r");
>
>then I can read OK from $fh, but if I try to use:
>
>    my $fh = Foo::foo("test.txt", "w");
>
>then I can't write to $fh - I get a warning that "Filehandle Foo::_GEN_0
>opened only for input".
>
>I believe this is because ExtUtils's "typemap" maps "FILE *" to "T_IN"
>which opens the file for reading only.

Perl needs to do different things for input and output and inout handles,
so one typemap for FILE * cannot solve the problem.

>
>Obviously I can't define the XS foo() to return different types (T_IN,
>T_OUT, T_INOUT) depending on what the "mode" argument was, 

No, but you can create the appropriate "Scalar Value" (SV *) and return 
that from your XS code - this is after all what xsubpp does with the typemap
stuff.

>so what is
>the best way around this?

Open the file in perl ?

>
>I tried returning the fileno() of the C file stream from the XS foo()
>and then effectively doing an fdopen() on that back in the Perl:
>
>    my $fd = Foo::foo("test.txt", "r");
>    open(my $fh, "<&=$fd");
>
>(in which the "<" is deduced from the "r").
>
>This works OK, 

Until you want to port to Win32 or some other place where fileno() is 
an alien concept which does not work very well.

>but it's not pretty, and what if a wanted a Perl wrapper
>to C's open()?  It's not so easy to work out the "(+)<|>|>>" mode for
>Perl's open() from the O_* flag argument to C's open().

It isn't that hard, and you could use sysopen() anyway which takes O_*
type args.

>
>Is there a nice, general way of doing this which I'm missing?

Not really. 

-- 
Nick Ing-Simmons
http://www.ni-s.u-net.com/

Reply via email to