Написание модуля perl xs - используйте другую функцию из того же файла xs

Я новичок в XS и потратил некоторое время на поиск этого ответа в Интернете, но безуспешно. Проблема в том, что XS меняет имя функции, и когда она пойдет на компиляцию, я получу ошибку неопределенной ссылки. Например, рассмотрим код XS ниже:

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

Затем я пытаюсь использовать это в следующей функции, подобной этой

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

Затем я запускаю make, и он проходит процесс компиляции, и я получаю сообщение об ошибке на этапе компоновки undefined reference to 'matrixIndex'.

Итак, мне интересно, каков стандартный способ XS для вызова функции из того же файла XS?


person Community    schedule 04.10.2017    source источник
comment
Если вы хотите вызвать функцию XS (в отличие от функции C ранее в файле), вам нужно будет вызвать ее как подпрограмму Perl. Не делайте это функцией XS, сделайте ее функцией C.   -  person ikegami    schedule 04.10.2017


Ответы (2)


Код XS создает подпрограммы Perl. Таким образом, вызов функции XS аналогичен вызову любой другой подпрограммы Perl.

Вместо того, чтобы иметь дело с этой сложностью и неэффективностью, создайте функцию C вместо подпрограммы Perl. (Вы можете независимо открыть эту функцию C с помощью XS, если хотите.)

#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

Вы используете return неправильно. Если вы хотите вернуться досрочно, используйте один из макросов XSRETURN*.

fromMatrix[...] и intoVector[...] совершенно неверны. fromMatrix и intoVector - это массивы C. (Они даже не массивы Perl, но это не важно.)

Целые числа Perl имеют размер IV (или UV для беззнаковых), не обязательно size_t. Используйте их для лучшей совместимости.

Если вам нужна переносимость, вы не можете предполагать C99, поэтому вы не можете смешивать объявления и код. Вам нужно поместить объявления в PREINIT (или использовать фигурные скобки в CODE, чтобы создать новую область для объявлений переменных).

person ikegami    schedule 04.10.2017
comment
Я понимаю. Осталось скомпилировать и связать. И полезная информация тоже. Спасибо! - person ; 04.10.2017
comment
fromMatrix[...] и intoVector[...] будут скомпилированы и слинкованы, но не будут работать. Вам все еще нужно это исправить. И я только что сделал обновление. - person ikegami; 04.10.2017
comment
Да, я искал в Интернете документацию по преобразованию из массива Perl в массив C и обратно в другом направлении. Любые хорошие ссылки на этот процесс? Поможет даже перенаправление на другие вопросы и ответы по стеку. - person ; 04.10.2017
comment
SvRV разыменовать скаляр, содержащий ссылку. SvTYPE можно использовать для проверки того, что указанная переменная на самом деле является массивом. Затем вы можете преобразовать переменную, возвращаемую SvRV, из SV* в AV*. newAV() создает массив. av_* функции работают с массивами. - person ikegami; 04.10.2017

XSUB не является функцией C. Препроцессор XS создает функцию C из ваших объявлений, но она имеет сигнатуру void (CV*). Затем XSUB берет аргументы из стека аргументов Perl и переводит их в значения C, используя ваши карты типов. Поэтому невозможно вызвать ваши XSUB напрямую.

Вместо этого вам пришлось бы вызывать XSUB, как если бы это была любая другая функция Perl, т. е. помещать аргументы как SV*s в стек. Подробнее см. perldoc perlcall. Понятно, что это утомительно и неэффективно.

Лучшее решение — объявить все функции, которые вы хотите использовать как из Perl, так и из XS, как static C-функции в файле XS, а затем написать связующий код XS, чтобы открыть его для Perl. Здесь:

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;
}

Обратите внимание, что если эта функция использует какую-либо часть Perl API, вам также следует использовать макросы pTHX и aTHX. Декларация меняется на

static size_t matrixIndex(pTHX_ size_t colIndex, etc...) ...

и при вызове из C вы бы написали matrixIndex(aTHX_ colIndex, etc...).

person amon    schedule 04.10.2017
comment
Да, я понял это. Определенно лучше заранее объявить функции C, которые мне нужны, а затем использовать XS в качестве клея, как вы говорите. Так тоже намного чище. У меня просто проблемы с сортировкой массивов взад и вперед. - person ; 04.10.2017
comment
@annoying_squid Это совсем другой вопрос — задайте его! - person amon; 04.10.2017