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

I've successfully made a shared library that can calculate directory sizes on Win32, and I can link to it via a 100% C utility. Now I want to build a perl module to access the lib. Unfortunately, I'm having some difficulties.

The function in the library is "_dir_size()" and the function I want to expose to perl is "dir_size()". The perl function will work like this:

my $hashref; my $result = dir_size("C:\\temp", $hashref); print "File count = ", $hashref->{FileCount}, "\n"; print "Dir size = ", $hashref->{LowSize}, "\n";

I have this XS function defined:

int dir_size (dirname,dirinfo,permsdie=0,otherdie=0) PREINIT: AV *errs = newAV(); HV *newdirinfo = newHV(); unsigned long hightotalsize = 0; unsigned long lowtotalsize = 0; long filecount = 0; long dircount = 0; INPUT: SV *dirinfo; char *dirname; int permsdie; int otherdie; CODE: RETVAL = _dir_size (errs, permsdie, otherdie, dirname, &highto +talsize, &lowtotalsize, &filecount, &dircount); hv_store(newdirinfo, "Errors", 6, newRV((SV *)errs), 0) +; hv_store(newdirinfo, "HighSize", 8, newSVuv(hightotalsize), + 0); hv_store(newdirinfo, "LowSize", 7, newSVuv(lowtotalsize +), 0); hv_store(newdirinfo, "FileCount", 9, newSViv(filecount), 0) +; hv_store(newdirinfo, "DirCount", 8, newSViv(dircount), 0); dirinfo = newRV_noinc((SV *)newdirinfo); printf("Found %i files\n", filecount); OUTPUT: dirinfo RETVAL

The printf() call in the above XS spits out the right number, but back in perl, my $hashref is undef. Any ideas?

Replies are listed 'Best First'.
Re: XS returning undef?
by jand (Friar) on Mar 09, 2003 at 08:07 UTC
    Instead of
    dirinfo = newRV_noinc((SV *)newdirinfo);
    try:
    sv_setsv(dirinfo, sv_2mortal(newRV_noinc((SV *)newdirinfo)));
    BTW, I think you should also change
    hv_store(newdirinfo, "Errors", 6, newRV((SV *)errs), 0);
    to use newRV_noinc() to keep the refcount of errs correct.
      Perfect! This did the trick, ++jand. I guess I should have thought of using sv_setsv(). Why does that work and not directly assigning the pointer?

      As for the newRV() vs. newRV_noinc(), that's what I get for copying code from perlxstut. I noticed in the very latest docs that it was deprecated, but I must have missed that one. Thanks again.

        You cannot swap the actual SV that the SV* in @_ is pointing to, as that SV itself is owned by the caller. @_ contains just *aliases* to the original parameters. Changing the SV*'s in @_ doesn't do anything to the value of the original parameter; they just go away when your function returns. (And since the stack isn't refcounted, you will leak memory too). You can only change the *value* of the SV that is being passed to you.
        Because SV's are C-structs consisting of 3 elements, first of which is a pointer to another struct ... see perlguts,Perl Guts Illustrated.

        update: what jand said. variables in c are pass by value.


        MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
        I run a Win32 PPM repository for perl 5.6x+5.8x. I take requests.
        ** The Third rule of perl club is a statement of fact: pod is sexy.

Re: XS returning undef?
by PodMaster (Abbot) on Mar 09, 2003 at 07:36 UTC
    What did you expect my $hashref to be?
    I assume 'RETVAL', which is what it should be (in scalar context).

    What is RETVAL supposed to be?
    If you wanted 'dirinfo', try list context.
    If you don't understand why that happened, read "List" is a Four-Letter Word.

    update: I see, try increasing the ref count(newRV_inc).


    MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
    I run a Win32 PPM repository for perl 5.6x+5.8x. I take requests.
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      I'm setting dirinfo to a hashref in this line:

      dirinfo = newRV_noinc((SV *)newdirinfo);

      And since dirinfo is listed in the OUTPUT section, I'm expecting my $hashref to actually be the hashref I created in the newRV_noinc() call. In the C code generated by xsubpp, it looks ok:

      ST(1) = dirinfo; SvSETMAGIC(ST(1));

      so obviously the second item on the stack (my $hashref) is getting set to dirinfo as it should. The only thing I can think of is that it's somehow being garabage collected too soon.

      RETVAL is an integer status code that I'm returning.

      Update: I checked on RETVAL ($result in my code example above) and it's getting set properly.