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

I've been messing around with Inline and XS recently to speed up some particularly slow routines in my roguelike engine. Some of the routines involve taking in arguments and returning them. When I started to get really strange segfault errors, I decided to reduce the code to a simple test case. When I did, I found out that it's difficult to take in a variable number of arguments and pass back multiple return values in the same C subroutine with XS macros. I'm sure that there is a way; I just have not yet discovered it. Could anybody please point me in the right direction?
#!/usr/bin/perl package perlicious; use strict; use warnings; use Data::Dumper; use Inline C => 'DATA', CCFLAGS => '-g', CLEAN_AFTER_BUILD => 0; sub debug { print Dumper([@_]), "\n" } print ">>>Calling in...<<<\n"; in("smack", 16, "that", "fool"); print "\n>>>Calling out...<<<\n"; debug out("smack", 16, "that", "fool"); print ">>>Calling combo...<<<\n"; debug buggy("smack", 16, "that", "fool"); # This just returns a list of numbers sub perlish { my $num = shift; my @ls; push @ls, "numba $_" foreach 1 .. $num; return @ls; } __DATA__ __C__ // All of this probably could be written better. I'm new to C and XS. +Critique // welcome. // This tests taking in a variable amount of arguments. Works fine. void in (char *first, ...) { SV *gotin[10]; int passed; dXSARGS; sp = mark; passed = items; int i; for (i = 0; i < items; i++) { gotin[i] = newSVsv( ST(i) ); } PUTBACK; int cntr; for (cntr = 0; cntr < passed; cntr++) { printf("gotin: %s\n", SvPV_nolen( gotin[cntr] )); } } // This demonstrates calling a perl sub and returning its results to p +erl. The // reason for the separate blocks is to avoid clashes with the macros +that set // up the stack variables. This is also why the temporary array gotbac +k is // needed. void out (SV* useless, ...) { SV *gotback[10]; int returned; if (1) { dSP; I32 ax; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs( sv_2mortal( newSViv(7) ) ); PUTBACK; returned = call_pv("perlish", G_ARRAY); printf("expect %d args\n", returned); SPAGAIN; SP -= returned; ax = (SP - PL_stack_base) + 1; int cnt; // Somehow store the return values. We need an array of SV*s. for (cnt = 0; cnt < returned; cnt++) { gotback[cnt] = newSVsv( ST(cnt) ); } PUTBACK; FREETMPS; LEAVE; } // Finally push the crap onto our return stack. if (1) { dXSARGS; sp = mark; int cnt; for (cnt = 0; cnt < returned; cnt++) { XPUSHs( gotback[cnt] ); } PUTBACK; return; } } // This tries to combine the two by taking a variable amount of argume +nts in // and returning a variable amount to perl. The input bit works, but t +he output // does not. (perl doesn't get any return values back on the stack.) T +his is // undoubtedly due to the fact that I use the dXSARGS, sp=mark, etc se +tup bit // twice. I don't know how to get around this. Help? // The solution may lie in the SPAGAIN, ENTER, LEAVE, etc macros which + some of // the documentation mentions. I'll get around to experimenting with t +hose // later. In the meantime, does anybody know exactly what to do? void buggy (char *first, ...) { // Tough to get around. Max arguments we can get back from a perl fu +nction // is gonna be 10. sorry. // Same for max we can take in here. SV *gotin[10]; SV *gotback[10]; int returned, passed; // First print args. Make sure I've got em! if (1) { dXSARGS; sp = mark; passed = items; int i; for (i = 0; i < items; i++) { gotin[i] = newSVsv( ST(i) ); } PUTBACK; int cntr; for (cntr = 0; cntr < passed; cntr++) { printf("gotin: %s\n", SvPV_nolen( gotin[cntr] )); } } if (1) { dSP; I32 ax; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs( sv_2mortal( newSViv(7) ) ); PUTBACK; returned = call_pv("perlish", G_ARRAY); printf("expect %d args\n", returned); SPAGAIN; SP -= returned; ax = (SP - PL_stack_base) + 1; int cnt; // Somehow store the return values. We need an array of SV*s. for (cnt = 0; cnt < returned; cnt++) { gotback[cnt] = newSVsv( ST(cnt) ); } PUTBACK; FREETMPS; LEAVE; } // Finally push the crap onto our return stack. if (1) { dXSARGS; sp = mark; int cnt; for (cnt = 0; cnt < returned; cnt++) { XPUSHs( gotback[cnt] ); } PUTBACK; return; } }

Replies are listed 'Best First'.
Re: Inline/XS multiple input and output parameters
by syphilis (Archbishop) on Mar 28, 2007 at 03:07 UTC
    See 'perldoc Inline::C-Cookbook'. Look for "Variable Argument Lists" and "Multiple Return Values". The Inline_Stack_Vars types are just aliases for various XS macros (see Inline.h in your script's build directory for the definitions of the Inline_Stack_Vars). You can, of course, use the XS macros in preference to Inline_Stack_Vars. And, when it comes to callbacks, you'll be forced to use XS macros as the Inline_Stack_Vars types are incomplete (and therefore often inadequate for callbacks).

    I haven't delved into the script you posted - so perhaps I've missed the point, but it's not (generally) difficult to both pass and return variable length lists - as demonstrated by the examples given in the Cookbook.

    Cheers,
    Rob