XS: sysread () look-alike?

I

Ivan Shmakov

I'm implementing a sysread () look-alike in XS:

my $result;
my $status
= $myio->myread ($start, $count, RESULT);

I wonder whether it's sensible to pass a scalar for RESULT (IOW,
$result), or should I use a reference instead (\$result)?

The issue with passing a scalar is that it won't (AIUI) be all
that easy to allow for "filter" or "guard" functions at the Perl
side, e. g.:

our $myio;
sub read_from_0 {
my $r
= $myio->myread (0, @_);
die (error_message ($r))
unless ($r == 0);
}

my $result;
read_from_0 ($count, $result);

Somehow, it seems that @_[1] in read_from_0 () won't be the same
scalar as the caller's $result. (While for \$result, it would
still be a /reference/ to the same scalar.)

Anything else I should consider?

TIA.
 
R

Rainer Weikusat

Ivan Shmakov said:
I'm implementing a sysread () look-alike in XS:

my $result;
my $status
= $myio->myread ($start, $count, RESULT);

I wonder whether it's sensible to pass a scalar for RESULT (IOW,
$result), or should I use a reference instead (\$result)?

I think you should work with the language you are using and not
against it: Perl is the opposite of C in this respect and passes
everything 'by reference'. Consequently, using scalars in the way
sysread does is totally fine. A better idea might be to return a
scalar with the contents or undef on error, possibly using an
optionally passed scalar for a more detailed status code. Or setting
$! to a sensible value. Or using the perlapi croak-routine for error
reporting.
 
I

Ivan Shmakov

[...]
A better idea might be to return a scalar with the contents or undef
on error, possibly using an optionally passed scalar for a more
detailed status code. Or setting $! to a sensible value. Or using
the perlapi croak-routine for error reporting.

In this case, the library I'm writing interface for has a host
of possible error codes, which do not map to errno.h codes, but
which I nevertheless wish to preserve.

I've thus decided that I'd closely follow the library's own
interface of returning a non-zero status code upon failure, and
also provide convenience Perl wrappers, which raise an exception
if the status returned is non-zero. Consider, e. g.:

package XXX::Qux 0.1;

sub check_rv {
my ($name, $code, $args) = @_;
my $r
= $code->(@$args);
die (XXX::Exception->new ($r, $name))
unless ($r == 0);
## .
return;
}

## assuming XS wrappers inhabit this very same namespace, which they
## probably shouldn't

sub foo {
## .
check_rv_ref (q"xxx_foo",
\&xxx_foo, \@_);
}

Naturally, the XXX::Exception object has ->xxx_err_no () and
->xxx_err_string () methods (providing access to the library's
own "errno" and "strerror" facilities), as well as the
conventional ->error () method, which is also the object's
"stringification" routine.

The only case I deviate from this convention is the one when the
function returns an (array) reference from the XS wrapper in the
case of success, like:

sub check_rv_ref {
my ($name, $code, $args) = @_;
my $r
= $code->(@$args);
die (XXX::Exception->new ($r, $name))
unless (ref ($r));
## .
$r;
}

sub bar_baz {
## .
check_rv_ref (q"xxx_bar_baz",
\&xxx_bar_baz, \@_);
}

This is the case when the xxx_bar () C function is to return
several values, typically via the storage pointed by its
arguments; as in:

xxx_error xxx_bar_baz (xxx_obj, int foo, int *bar, int *baz);

This corresponds to the following XS wrapper usage:

my $r
= $xxx_obj->xxx_bar_baz (42);
die ()
unless (ref ($r));
my ($bar, $baz)
= @$r;
 
I

Ivan Shmakov

Ben Morrow said:
[...]
I've thus decided that I'd closely follow the library's own
interface of returning a non-zero status code upon failure, and also
provide convenience Perl wrappers, which raise an exception if the
status returned is non-zero. Consider, e. g.:
It might be more convenient to do this via autodie. See
autodie::hints.

How do I get the library's function called (so to convert the
error code returned into a meaningful message) when using
autodie?
 
I

Ivan Shmakov

Ben Morrow said:
How do I get the library's function called (so to convert the error
code returned into a meaningful message) when using autodie?
[...]

Ideally, you would want to be able to subclass autodie::exception and
arrange to pick up the xxx-specific error information at creation
time. I'm not sure if it's possible to make autodie use your
subclass automatically, though. You might want to talk to Paul; when
I had suggestions in the past for improving the interface, he was
very willing to listen.

ACK, thanks.
Alternatively, you could just forget the idea of providing a
return-value interface, and just always throw your own exceptions.
If you do that, I would recommend making them objects rather than
strings, because it makes it possible to reliably distinguish them
from other exceptions when they're caught;

Unless I'm missing something, this is exactly what I do. The
point is that I'm hesitant to either generate exception objects
/or/ call Perl code from XS, so my XS ("low-level") interface
returns error codes, which the Perl wrapper methods then turn
into proper exceptions (which are indeed objects.)
if your exception class doesn't inherit from something like
Exception::Class, it needs to provide overloaded stringify,

The latter is also done.
and it needs to record at least file and line from caller at the
point where it's thrown.

I don't seem to understand this one. Any specific examples,
please?
 
I

Ivan Shmakov

I would pass a reference, because it makes it clearer that the
function modifies its argument.

Yes.

Yet, this seem to go against the convention already set by the
Perl core...

[...]
read_from_0 ($count, $result);
Somehow, it seems that @_[1] in read_from_0 () won't be the same
scalar as the caller's $result.
It will be. Both $_[1] in read_from_0 and the-equivalent-of-$_[2] in
myread will be aliases to $result.

ACK, thanks. Indeed, my case was more like:

sub myread {
## .
check_rv (q"lowlevel_myread",
\&lowlevel_myread, \@_);
}

with check_rv () later doing $_[1]->(@{$_[2]}).

Alas, in this latter case, it doesn't seem to work anymore.

So, the question is: is there any way to allow for such a use,
apart from resorting to use a reference (\$result)?
I would put the buffer argument first, to match IO::Handle->sysread.

It makes sense, indeed, though in this case I'd rather duplicate
the signature of the library's function I'm writing the
interface for, so not to confuse those already familiar with the
C one.
 
R

Rainer Weikusat

Ivan Shmakov said:
Yes.

Yet, this seem to go against the convention already set by the
Perl core...

The reason for this is that the people who wrote 'the Perl core'
created a language which doesn't support passs-by-value. Because of
this, it is not necessary to pass a special 'derived value' in order
to have 'output arguments' and dealing with output arguments requires
no 'special indirection syntax'.

[...]
sub myread {
## .
check_rv (q"lowlevel_myread",
\&lowlevel_myread, \@_);
}

with check_rv () later doing $_[1]->(@{$_[2]}).

Alas, in this latter case, it doesn't seem to work anymore.

perl -e 'sub nice { $_[0] = 1723; }; sub try { nice(@{$_[0]}); } sub watson { try(\@_); }; watson($r); print $r'

Given the way argument passing works in Perl (by putting pointers to
SVs on the stack), it is - unfortunately - not possible to weasel
around pass-by-reference with Perl code in this way: At some point,
you have to make a copy of the argument in question in order to create
a different scalar/ SV.

[...]
It makes sense, indeed, though in this case I'd rather duplicate
the signature of the library's function I'm writing the
interface for, so not to confuse those already familiar with the
C one.

Chances are that your Perl code is encountered by Perl programmers ...
 
I

Ivan Shmakov

Ivan Shmakov said:
[...]
read_from_0 ($count, $result);
Somehow, it seems that @_[1] in read_from_0 () won't be the same
scalar as the caller's $result.
It will be. Both $_[1] in read_from_0 and the-equivalent-of-$_[2]
in myread will be aliases to $result.
ACK, thanks. Indeed, my case was more like:
sub myread {
## .
check_rv (q"lowlevel_myread",
\&lowlevel_myread, \@_);
}
with check_rv () later doing $_[1]->(@{$_[2]}).
Alas, in this latter case, it doesn't seem to work anymore.

... Apparently, I've essentially confused \@_ with [ @_ ], and
the first one indeed works as expected.

Sorry for the noise.

[...]
 
R

Rainer Weikusat

Ivan Shmakov said:
Quoth Ivan Shmakov <[email protected]>:
[...]
read_from_0 ($count, $result);
Somehow, it seems that @_[1] in read_from_0 () won't be the same
scalar as the caller's $result.
It will be. Both $_[1] in read_from_0 and the-equivalent-of-$_[2]
in myread will be aliases to $result.
ACK, thanks. Indeed, my case was more like:
sub myread {
## .
check_rv (q"lowlevel_myread",
\&lowlevel_myread, \@_);
}
with check_rv () later doing $_[1]->(@{$_[2]}).
Alas, in this latter case, it doesn't seem to work anymore.

... Apparently, I've essentially confused \@_ with [ @_ ], and
the first one indeed works as expected.

The second one also works as expected: You are copying the contents of
@_, hence, you get a different set of scalars.
 
I

Ivan Shmakov

Rainer Weikusat said:
[...]

dealing with output arguments requires no 'special indirection
syntax'.

Which is what I consider a serious drawback of the language.
(The same applies to Fortran and C++, though.)

[...]
Chances are that your Perl code is encountered by Perl programmers
...

Given the (planned) incompleteness of the interface (at least
for the first N releases), I'd expect for its users to check
with the interfaced library's own documentation from time to
time. And having two different interfaces to stumble upon
doesn't seem like some clever time-saving feature.

That doesn't mean that there can't be a more "Perl-friendly"
interface on top of it, though.
 
I

Ivan Shmakov

Ben Morrow said:
[...]
Unless I'm missing something, this is exactly what I do. The point
is that I'm hesitant to either generate exception objects /or/ call
Perl code from XS, so my XS ("low-level") interface returns error
codes, which the Perl wrapper methods then turn into proper
exceptions (which are indeed objects.)
Ah, OK. I though you were trying to provide both return-value and
exception interfaces so users could choose which they wanted.

FWIW, I'm going to document both, but I don't expect for the
users to actually use the return-value interface. (Other than
in the cases the respective Perl wrapper methods are found to be
faulty or otherwise flawed.)
Throwing exceptions from XS is fairly straightforward; you build your
object (however you like) and call Perl_croak_sv. However it would
probably be easier to have a My::Exception->throw method, which
builds and throws an exception; this means you just need to make a
method call, which goes like this:

... Which is exactly what I'm trying to avoid. Debugging XS
code isn't all that easy to me, and I'm pretty sure that having
to debug XS code paths intertwined with Perl's won't be of any
help to me. Or to any inexperienced Perl user who may happen to
have to debug my code, for that matter.

[...]
and an exception of this class reaches the top-level 'print a message
and kill the program' exception handler, that message will not
include the 'at file ... line ...' information. Adding that
information is something die does if you pass it a string; if you
pass it an object, it has nowhere to put the information.
This means that your 'new' method needs to record that information,
somehow, so it can include it in the stringification.

ACK, thanks for the information.

For now, I'm effectively "substituting" die () with croak (),
like:

package main;
require Carp;
$SIG{"__DIE__"}
= \&Carp::confess;

for the "at file" error messages weren't so far nearly as
helpful as complete stack traces, and my guess is that it also
solves the issue at hand. Thus, I'm going to postpone this one
until some later time.
You can pull it out of 'caller', though that means working out how
many stack frames to go back to get to the 'real' location of the
error.

Yes, and it seems that there is indeed some choice.
You can also use Carp::shortmess, which gives you the message 'croak'
would have given you.

It doesn't seem documented, does it?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top