mugwumpjism has asked for the wisdom of the Perl Monks concerning the following question:
Can anyone tell me why this XS routine returns my RETVAL with one extra depth of array refs?
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
SV *
readdir_inode(dirname)
char* dirname
INIT:
struct dirent *ent;
DIR* dir;
SV* record[2];
AV *entry, *ret_val;
CODE:
ret_val = newAV();
dir = opendir(dirname);
if (!dir)
perror("Cannot open dir");
while ((ent=readdir(dir))) {
record[0] = newSVpv(ent->d_name, 0);
record[1] = newSViv((IV)ent->d_ino);
entry = av_make(2, record);
av_push(ret_val, newRV_inc((SV*) entry));
}
closedir(dir);
RETVAL=newRV_inc((SV*) ret_val);
OUTPUT:
RETVAL
That is, I'm getting a structure like this:
$VAR1 = [
[
[
'.',
67586
],
[
'..',
255604
],
...
Instead of something like this:
$VAR1 = [
[
'.',
67586
],
[
'..',
255604
],
...
Any ideas why, anyone?
Also, does anyone know of a better way of saying "or die "Can't opendir $file; $!"" (or should I bother? perhaps just return undef and let the caller check $!)
Re: XS routine returns an extra depth of array refs
by Fastolfe (Vicar) on Nov 05, 2001 at 22:04 UTC
|
Let's see your Perl code where you get the results of this function and do a Dumper on it. I expect to see:
$ref = readdir_inode($dirname);
print Dumper($ref);
# or
@a = readdir_inode($dirname);
print Dumper($a[0]); # not Dumper(\@a)
I don't see anything obviously wrong with your code, but I want to eliminate some assumptions first before I go digging. :) | [reply] [d/l] |
|
You hit the nail right on the head... I had the second, but Dumper \@a instead of Dumper $a[0], and my test code was assuming I was returning a list. Hate it when that happens ;-)
OK, so now at least I have it returning something sensible, which is always good. In which case, the question is why does this:
AV *
readdir_inode(dirname)
char* dirname
INIT:
struct dirent *ent;
DIR* dir;
SV* record[2];
AV *entry, *ret_val;
CODE:
RETVAL = newAV();
dir = opendir(dirname);
if (!dir)
perror("Cannot open dir");
while ((ent=readdir(dir))) {
printf("%ld %x\n", ent->d_ino, ent->d_name);
record[0] = newSVpv(ent->d_name, 0);
record[1] = newSViv((IV)ent->d_ino);
entry = av_make(2, record);
av_push(RETVAL, newRV_inc((SV*) entry));
}
closedir(dir);
/*RETVAL=*ret_val;*/
/*RETVAL=newRV_inc((SV*) ret_val);*/
OUTPUT:
RETVAL
Return an extra level of arrays?
The calling code:
my @ents = readdir_inode(".");
print Dumper @ents;
The output:
$VAR1 = [
[
'.',
67586
],
[
'..',
255604
... | [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: XS routine returns an extra depth of array refs
by Fastolfe (Vicar) on Nov 05, 2001 at 22:09 UTC
|
Returning 'undef' from your function on error and setting $! seems the best way of doing this. If you really want to throw an error directly from your XS, use the "croak" function, which behaves like die in Perl. (There's also a 'warn'.) These act like printf.
croak("Can't opendir %s!", dirname);
| [reply] [d/l] [select] |
Re: XS routine returns an extra depth of array refs
by jeroenes (Priest) on Nov 05, 2001 at 22:28 UTC
|
Maybe the casting of entry to an SV does the trick. newRC_inc can also take an AV as input.
Update: Tested the following... too lazy for XS, so
Inline:
use Data::Dumper;
$ref = testRV( 'monks' );
print Dumper( $ref );
use Inline C => <<'END_OF_C'
SV* testRV(char* dirname){
SV* record[2];
AV *entry, *ret_val;
SV* retarray;
int i = 0;
ret_val = newAV();
while (i<10) {
record[0] = newSVpv(dirname, 0);
record[1] = newSViv(i);
entry = av_make(2, record);
av_push(ret_val, newRV_inc( entry));
i++;
}
retarray=newRV_noinc( ret_val);
return retarray;
}
END_OF_C
#results in:
$VAR1 = [
[
'monks',
'0'
],
[
'monks',
1
],
[
'monks',
2
],
...
In other words, just a 2D array as you wanted. BTW,
Inline didn't complain about the AV* 's, but than, maybe
those warnings were suppressed?
So I think Fastolfe has pinpointed it, when he noticed
you exc'd the sub in array context...
If you really want to output arrays from C, you need
to push your items on the stack.
| [reply] [d/l] |
|
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
void
readdir_inode(dirname)
char* dirname
INIT:
struct dirent *ent;
DIR* dir;
SV* record[2];
AV *entry, *ret_val;
PPCODE:
dir = opendir(dirname);
if (dir) {
while ((ent=readdir(dir))) {
printf("%ld %x\n", ent->d_ino, ent->d_name);
record[0] = newSVpv(ent->d_name, 0);
record[1] = newSViv((IV)ent->d_ino);
PUSHs(newRV_noinc(av_make(2, record)));
}
closedir(dir);
}
At least, I hope that last "newRV_noinc" shouldn't be a "newRV_inc"! | [reply] [d/l] [select] |
|
It should be newRV_inc. :-)
Only with inline the refcounter of the SV gets auto-incremented. With XS you have to that yourself.
Update: For clarity, only the refcounter of the SV that gets returned! Other SV*'s you have to inc manually!
| [reply] |
|
|
|
ReadDir.xs: In function `XS_ReadDir_readdir_inode':
ReadDir.xs:43: warning: passing arg 1 of `Perl_newRV' from incompatibl
+e pointer type
| [reply] [d/l] |
|
|