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

Hi,
I'm calling perl functions from within C according to:
http://www.perl.com/doc/manual/html/pod/perlcall.html
Now I'm trying to figure out how to pass variable arguments from C to perl, for example
void foo (int a, ...) { dSP ; ENTER ; SAVETMPS ; PUSHMARK(SP) ; va_list args; va_start(args, a); // how do I pass the variable arguments to Perl? XPUSHs(sv_2mortal(newSViv(a))); va_end(args); PUTBACK; perl_call_pv("My::Func", G_DISCARD); FREETMPS; LEAVE; }
Anyone know how to do this / if it is possible? Note that I don't know how many variable arguments are passed ('a' is just some other number).
Thanks

Replies are listed 'Best First'.
Re: calling from C - variable argument list
by BrowserUk (Patriarch) on Aug 04, 2009 at 05:28 UTC

    The rules of using the varargs macros dictate that either:

    • The first argument tells you how many arguments are passed.
    • Or the last argument passed is some kind of sentinal value that tell you when you have that last argument.

    Here's an example of using the latter:

    #! perl -slw use strict; use Inline C => <<'END_C', NAME => 'varArgsC2P', CLEAN_AFTER_BUILD => + 0; void foo (int first, ...) { dSP ; int next; va_list argp; ENTER ; SAVETMPS ; PUSHMARK(SP) ; va_start( argp, first ); XPUSHs( sv_2mortal( newSViv( first ) ) ); while( ( next = va_arg( argp, int ) ) != -1 ) { XPUSHs( sv_2mortal( newSViv( next ) ) ); // printf( "Stacking %d \n", next ); } va_end( argp ); PUTBACK; perl_call_pv( "main::test", G_SCALAR ); // SPAGAIN; FREETMPS; LEAVE; } void call_foo( int n ) { switch( n ) { case 1: foo( 1, -1 ); break; case 2: foo( 1,2, -1 ); break; case 3: foo( 1,2,3, -1 ); break; case 4: foo( 1,2,3,4, -1 ); break; case 5: foo( 1,2,3,4,5, -1 ); break; case 6: foo( 1,2,3,4,5,6, -1 ); break; case 7: foo( 1,2,3,4,5,6,7, -1 ); break; case 8: foo( 1,2,3,4,5,6,7,8, -1 ); break; case 9: foo( 1,2,3,4,5,6,7,8,9, -1 ); break; case 10: foo( 1,2,3,4,5,6,7,8,9,10, -1 ); break; } return; } END_C sub test { printf "test called with %d args: [@_]\n", scalar @_; } call_foo( $_ ) for 1 .. 10; __END__ C:\test>varArgsC2P.pl test called with 1 args: [1] test called with 2 args: [1 2] test called with 3 args: [1 2 3] test called with 4 args: [1 2 3 4] test called with 5 args: [1 2 3 4 5] test called with 6 args: [1 2 3 4 5 6] test called with 7 args: [1 2 3 4 5 6 7] test called with 8 args: [1 2 3 4 5 6 7 8] test called with 9 args: [1 2 3 4 5 6 7 8 9] test called with 10 args: [1 2 3 4 5 6 7 8 9 10]

    Note that I'm calling call_foo( n ) from Perl, so that C can callback to main::test with the specified number of arguments.

    Note also that you can only use the sentinal value method if a) all the arguments are of the same type--you have to call the va_arg() macro with the appropriate (hardcoded) type; b) if there is a suitable sentinal value.

    Finally, I'm not sure whether the SPAGAIN macro should be called. Some examples show it, but I've never found an explanation of what it does or when/why you should call it. The code seems to work with and without it.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: calling from C - variable argument list
by Anonymous Monk on Aug 04, 2009 at 01:39 UTC

    Every single Perl sub calls takes an arbitrary number of arguments. There's nothing special to it. Just push every argument like in the example under "Passing Parameters" in perlcall

    - ikegami

Re: calling from C - variable argument list
by syphilis (Archbishop) on Aug 04, 2009 at 02:05 UTC
    // how do I pass the variable arguments to Perl?

    You should be able to do what you want by making use of items, which is set to the number of arguments being passed:
    use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; void foo(SV * a, ...) { dXSARGS; int i, t; /* add new values onto the end of the stack */ for(i = 0; i < items; i++) { t = SvIV(ST(i)) * 3; XPUSHs(newSViv(t)); } for(i = 0; i < items * 2; i++) { printf("%d\n", SvIV(ST(i))); } } void bar(SV * a, ...) { dXSARGS; int i, t; /* overwrite the existing stack values with new ones */ sp = mark; for(i = 0; i < items; i++) { t = SvIV(ST(i)) * 3; XPUSHs(newSViv(t)); } for(i = 0; i < items; i++) { printf("%d\n", SvIV(ST(i))); } } EOC foo(1 .. 5); print "\n"; bar(1 .. 4);
    Cheers,
    Rob
Re: calling from C - variable argument list
by plobsing (Friar) on Aug 04, 2009 at 02:03 UTC

    If you don't know how many arguments are being passed, how do you expect perl to know? My understanding of varargs in C is that you have to somehow determine how many elements got passed. Note that this is not a limitation of perl, but of C (or my knowledge of C).