cperlundefined-referencexs

perl xs module writing - Use another function from within same xs file


I'm a beginner in XS and have spent some time looking for this answer on the web with no luck. The problem is that XS changes the name of the function and when it goes to compile, I will get an undefined reference error. For example consider the XS code below:

size_t 
matrixIndex (colIndex, rowIndex,nCols,nRows)
      size_t colIndex
      size_t rowIndex
      size_t nCols
      size_t nRows
    CODE:
    size_t register i;
    RETVAL = (rowIndex * nCols) + colIndex;
    OUTPUT:
        RETVAL

I then try to use this in the following function like this

int
matrixCopyColumnVector_dbl (colIndex,fromMatrix,nColsMatrix,nRowsMatrix,intoVector,nRowsVector)
      size_t colIndex
      SV * fromMatrix
      size_t nColsMatrix
      size_t nRowsMatrix
      SV * intoVector
      size_t nRowsVector
    CODE:
      size_t register x, n;
      if( nRowsVector != nRowsMatrix) { RETVAL = 0; return RETVAL; }
      n = 0;
      for(x=0; x<= nRowsMatrix; x++) {
         intoVector[n] = fromMatrix[matrixIndex /*USE OF FUNCTION HERE!!*/(colIndex,x,nColsMatrix,nRowsMatrix)];
         n++;
      }
      RETVAL = 1;
      return RETVAL;
    OUTPUT:
       RETVAL

I then run make and it goes through the compile process and I get an error at what appears to be the linking stage of undefined reference to 'matrixIndex'.

So I am wondering what is the standard XS way to call a function from within the same XS file?


Solution

  • XS code creates Perl subs. So calling an XS function is the same as calling any other Perl sub.

    Instead of dealing with that complexity and inefficiency, create a C function instead of a Perl sub. (You can independently expose that C function using XS if you want to.)

    #define PERL_NO_GET_CONTEXT
    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"
    
    static UV matrixIndex(UV colIndex, UV rowIndex, UV nCols, UV nRows) {
        return (rowIndex * nCols) + colIndex;
    }
    
    MODULE = Foo::Bar  PACKAGE = Foo::Bar
    
    int
    matrixCopyColumnVector_dbl(colIndex, fromMatrix, nColsMatrix, nRowsMatrix, intoVector, nRowsVector)
        UV colIndex
        SV * fromMatrix
        UV nColsMatrix
        UV nRowsMatrix
        SV * intoVector
        UV nRowsVector
    PREINIT:
        UV register x, n;
    CODE:
        if (nRowsVector == nRowsMatrix) {
            RETVAL = 0;
        } else {
            n = 0;
            for (x=0; x<=nRowsMatrix; x++) {
                intoVector[n] = fromMatrix[matrixIndex(colIndex, x, nColsMatrix, nRowsMatrix)];
                n++;
            }
            RETVAL = 1;
        }
    OUTPUT:
        RETVAL
    

    Your use of return is incorrect. If you want to return prematurely, use one of the XSRETURN* macros.

    fromMatrix[...] and intoVector[...] are completely wrong. fromMatrix and intoVector are C arrays. (They aren't even Perl arrays, not that that's relevant.)

    Perl integers are of size IV (or UV for unsigned), not necessarily size_t. Use those for best compatibility.

    If you want portability, you can't assume C99, so you can't mix declarations and code. You need to put declarations in PREINIT (or use curlies in CODE to create a new scope for variable declarations).