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

I'm fairly experienced when it comes to creating XS interface to perl libraries, but this issue has confounded me for the past few days. I want to take advantage of the "INTERFACE:" directive in one of my XS files, but for some reason the generated C will not compile. I made a test module with only the barest of essentials as a sanity check, and I still couldn't get the thing to make. I'm not sure what I'm doing wrong; everything looks like it should according to the perlxs manual.

Any information on this is much appreciated :-) Below is the code int my Test.xs file

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" int add_one(int i) { return i + 1; } int add_two(int i) { return i + 1; } MODULE = Test PACKAGE = Test int add_interface(i) int i INTERFACE: add_one add_two

the following is the error I get from cc during my make

cc -c -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -xO3 -xdepend -DV +ERSION=\"0.01\" -DXS_VERSION=\"0.01\" -KPIC -I/usr/perl5/5.6.1/lib/s +un4-solaris-64int/CORE Test.c "Test.c", line 34: syntax error before or at: cv "Test.c", line 45: type specifier can not be used as array size expres +sion qualifier "Test.c", line 45: warning: no explicit type given "Test.c", line 46: cannot recover from previous errors cc: acomp failed for Test.c *** Error code 2 make: Fatal error: Command failed for target `Test.o'

And finally here is the section of the generated C file it refers to

XS(XS_Test_add_interface) { dXSARGS; dXSFUNCTION(int); if (items != 1) Perl_croak(aTHX_ "Usage: Test::add_interface(i)"); { int i = (int)SvIV(ST(0)); int RETVAL; dXSTARG; XSFUNCTION = XSINTERFACE_FUNC(int,cv,XSANY.any_dptr); RETVAL = XSFUNCTION(i); XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); }

Replies are listed 'Best First'.
Re: perlxs INTERFACE problem
by syphilis (Archbishop) on Nov 15, 2007 at 03:32 UTC
    Hi Fiftyvolts,

    That XS file compiles fine for me on Win32 (perl 5.8.8) - and the resultant module tests fine.

    I do note that I'm getting a slightly different C file. Here it is (in full):
    /* * This file was generated automatically by ExtUtils::ParseXS version +2.17 from the * contents of Test.xs. Do not edit this file, edit Test.xs instead. * * ANY CHANGES MADE HERE WILL BE LOST! * */ #line 1 "Test.xs" #include "EXTERN.h" #include "perl.h" #include "XSUB.h" int add_one(int i) { return i + 1; } int add_two(int i) { return i + 1; } #ifndef PERL_UNUSED_VAR # define PERL_UNUSED_VAR(var) if (0) var = var #endif #line 28 "Test.c" XS(XS_Test_add_interface); /* prototype to pass -Wmissing-prototypes * +/ XS(XS_Test_add_interface) { #ifdef dVAR dVAR; dXSARGS; #else dXSARGS; #endif dXSFUNCTION(int); if (items != 1) Perl_croak(aTHX_ "Usage: %s(%s)", "Test::add_interface", "i"); PERL_UNUSED_VAR(cv); /* -W */ { int i = (int)SvIV(ST(0)); int RETVAL; dXSTARG; XSFUNCTION = XSINTERFACE_FUNC(int,cv,XSANY.any_dptr); RETVAL = XSFUNCTION(i); XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); } #ifdef __cplusplus extern "C" #endif XS(boot_Test); /* prototype to pass -Wmissing-prototypes */ XS(boot_Test) { #ifdef dVAR dVAR; dXSARGS; #else dXSARGS; #endif char* file = __FILE__; PERL_UNUSED_VAR(cv); /* -W */ PERL_UNUSED_VAR(items); /* -W */ XS_VERSION_BOOTCHECK ; { CV * cv ; cv = newXS("Test::add_two", XS_Test_add_interface, file); XSINTERFACE_FUNC_SET(cv,add_two) ; cv = newXS("Test::add_one", XS_Test_add_interface, file); XSINTERFACE_FUNC_SET(cv,add_one) ; } XSRETURN_YES; }
    Btw, note that *both* add_one() and add_two() add *one*. (Had me stumped for a moment when I was testing the module, because I expected add_two() to add two and hadn't actually read the relevant code ... silly me :-)

    Cheers,
    Rob
    Afterthought: Are you using the same compiler that was used to build perl ? If not, then could it be that your 'cc' is incompatible with the C code that's being generated ?

      Yeah it's the same compiler. Something else must be wrong... maybe I'm missing an option. I'm going to keep trying.

      As for add_one/add_two I ment that one was the first and one was the second, not what they return >.< Silly me, I think they get the award for worst function names of the day.

        Ok I think I have the solution. It appears to be a combination of my compiler and my XSUB.h file (which I guess is really a statement of my perl version 5.6.1)

        So 5.6.1 defines the macro that extracts the C function to call for a particular invocation of the perl interface something to the effect of this: (I expanded some of the macros for clarity)

        XSFUNCTION = ((ret (*cv)())(XSANY.dptr));

        Of course the cast shown above grossly violates the C standard for abstract declarators (no identifiers should be present, a.k.a. "cv"). The reason for this is because the header is reusing the same code that declares XSFUNCTION (which needs an identifier). cc barfs when it reaches the cast.

        I took a look at the XSUB.h in 5.8.8, and sure enough it's been corrected to not include an identifier. I must not be the first person with this problem :)

        If you are unfortunate enough to not be able to upgrade your version of perl for some reason and you still need or want to get this to work here's what you can add at the beginning of your XS file to fix things:

        #undef XSINTERFACE_FUNC #define XSINTERFACE_FUNC(ret,cv,f) ((ret (*)())(f))

        And that's all folks. Thanks for the advice :)

        Yeah it's the same compiler

        Seems an odd error to be getting. I just tried it out on linux and it all works fine there, too.

        What version of perl are you running ? I notice, too, that my C file is being generated by ExtUtils::ParseXS-2.17. Maybe that's the key to success ?

        Update: Then again ... on linux, the C file was generated by xsubpp-1.9508.

        Cheers,
        Rob