问题
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?
回答1:
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).
回答2:
An XSUB is not a C function. The XS preprocessor does create a C function from your declarations, but it has the signature void (CV*)
. The XSUB then takes arguments from the Perl argument stack and translates them to C values using your typemaps. It is therefore not possible to call your XSUBs directly.
Instead, you would have to call the XSUB as if it were any other Perl function, i.e. push the arguments as SV*
s onto the stack. See perldoc perlcall for details. Clearly this is tedious and inefficient.
A better solution is to declare all functions that you want to use from both Perl and XS as static
C functions in your XS file, then write the XS glue code to expose it to Perl. Here:
static size_t matrixIndex(size_t colIndex, size_t rowIndex, size_t nCols, size_t nRows)
{
size_t register i; // unneeded
return (rowIndex * nCols) + colIndex;
}
Note that if this function uses any part of the Perl API, then you should also use the pTHX
and aTHX
macros. The declaration changes to
static size_t matrixIndex(pTHX_ size_t colIndex, etc...) ...
and when calling it from C you'd write matrixIndex(aTHX_ colIndex, etc...)
.
来源:https://stackoverflow.com/questions/46569264/perl-xs-module-writing-use-another-function-from-within-same-xs-file