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

The documentation on perlxs seems to say that by designating a parameter "OUTPUT:", its value may be changed in the called C function and even though it is not a traditional return value, the corresponding variable in the calling Perl script will contain the new value. I am passing a scalar through a Perl extension to a C function, where (as an integer) it is changed. When control returns to the Perl script, the value is the same as it was before the function was called. I just can't seem to get this to work.
.XS file:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <TestInclude.h> MODULE = TEST PACKAGE = TEST void C_function(iReturn) int iReturn OUTPUT: iReturn
C function:
#include "TestInclude.h" void C_function(int iReturn) { iReturn = 5; }
.H prototype:
void  C_function(int      iReturn);

.PM file:
package TEST; require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT = qw(C_function); bootstrap TEST; 1;
Test Script:
use TEST; $Code = 25; C_function($Code); printf("Return Code: %d\n", $Code);
Please help. Thanks!! -Tim Farnsworth

Replies are listed 'Best First'.
Re: Perl extension parameters (by ref)
by tye (Sage) on Mar 09, 2004 at 00:13 UTC

    The XS code could modify its iReturn and output the changes. Your C code is modifying an iReturn that is simply a copy and those changes don't leave the C code and XS can't do anything to change that.

    If you want to have a C routine change a parameter, then you have to pass the parameter by reference.

    .XS file: #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <TestInclude.h> MODULE = TEST PACKAGE = TEST void C_function(iReturn) int &iReturn OUTPUT: iReturn C function: #include "TestInclude.h" void C_function( int *iReturn ) { *iReturn = 5; } .H prototype: void C_function( int *iReturn );

    Untested.

    - tye        

      Thank you, Tye. I'd gone through passing by reference and struggled with it for a week before I saw the documentation from perlxs and gave up thinking that there was an easier way (I was wrong). The thing that hung me up, and actually still bothers me is that the Perl script is passing a value, and the corresponding C code parameter is a pointer. We dereference the pointer and change the value in the space to which it is pointing, and when we get back to the Perl script, the parameter is a value again. To me this is counter intuitive. I would think that you would pass a reference from Perl and that would translate into a C pointer. Could you un-confuse me on this point? Thanks again for your help!!!

        Arguments to Perl subroutines are passed by alias. That is a lot like 'pass by reference' but not quite exactly and the word 'reference' in 'pass by reference' is only a bit related to the word 'reference' in 'Perl reference'.

        The "OUTPUT:" tells the XS code to do the equivalent of $_[0] = .... No need to pass in a reference. For regular Perl subroutines, it is often a good idea to use references rather than relying on 'pass by alias' (it often leads to clearer code), but for XS subroutines it would mostly add complication and more places to introduce bugs.

        A Perl reference wouldn't translate into a C pointer, at least not an 'int *'. It'd give you a pointer to a perl struct (RV) that contained the reference and that you could use to get a pointer to another perl struct (SV) that contains the Perl scalar value. But your current code already gives you this SV* directly. With it, you can copy out an integer value or copy in a new integer value.

        The XS system generates code to copy the integer value out of the scalar and into your C variable, iReturn (which then gets passed to the C subroutine). Since you specified "OUTPUT: iReturn", code is also generated to copy the value from iReturn back into the scalar.

        Note that the scalar might not have originally contained an integer value (IV) so the first step above might prompt a conversion from the scalar's string or floating-point value into an integer (which gets cached in the SV along with any other types of values that the scalar already has).

        When the new integer value gets copied back into the scalar, then any values of other types cached in the scalar are discarded.

        - tye