Почему я получаю segfault при вызове моей функции C ++ с .Call, а не. C? - PullRequest
6 голосов
/ 07 апреля 2020

Моя конечная цель - вызвать некоторые функции C ++ изнутри R, вот MNWE, где я сталкиваюсь с препятствиями. Если я правильно читаю комнату, мне кажется, что при вызове моей функции с помощью .Call у меня возникает ошибка, но когда я вызываю ее с .C, все работает нормально.

Вот моя короткая функция C ++

// test.cpp
#include <iostream>

extern "C" void fnTest() {
  std::cout << "Hello" << std::endl;
}

Который я затем скомпилировал с

R CMD SHLIB -o test.so test.cpp

, который дал следующий вывод:

g++ -std=gnu++11 -shared -L/usr/lib64/R/lib -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -o te
st.so test.o -L/usr/lib64/R/lib -lR

Теперь внутри RI сделал

> dyn.load("test.so")
> .C("fnTest")
Hello
list()
> .Call("fnTest")
Hello

 *** caught segfault ***
address 0x30, cause 'memory not mapped'

Possible actions:
1: abort (with core dump, if enabled)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection:

Документация, которую я прочитал для этих двух функций, здесь и, похоже, не указывает на большую разницу в формате вызова этих двух функций. Я попробовал несколько других вариантов (например, я был в состоянии успешно передать аргументы .C, но не .Call) и не имел никакого успеха.

Как правильно .Call функция C ++ изнутри R?

Некоторые замечания по моему возможному варианту использования, помимо этого минимального примера, надеюсь, это не проблема XY : у меня есть проект со многими сложными зависимостями, которые я знаю, как построить с CMake, но не напрямую из g ++. Я смог создать из этого проекта общую библиотеку, которую затем мог бы связать с общей библиотекой «R-совместимый» (R CMD SHLIB -o test.so test.cpp -L/path/to/my/lib/ -l my_lib_name), которую я смог dyn.load() добавить в свою среду R. В этот момент я столкнулся с проблемой .C против .Call.

1 Ответ

1 голос
/ 07 апреля 2020

Из прочтения некоторой дополнительной документации (которую я должен был найти при первом проходе) я считаю, что вы не можете .Call функцию, которая имеет тип возврата void. Я не смог найти явного упоминания об этом, но ни в одном примере в документации (например, this section ) не указан тип возврата, отличный от SEXP, и в какой-то момент в документации говорится, что:

Все объекты R, с которыми вы будете иметь дело, будут обрабатываться с типом SEXP

С другой стороны, как описано в Интерфейсных функциях. C и .Fortran. , любая функция, которую вы .C должны иметь возвращаемый тип void:

Обратите внимание, что скомпилированный код не должен возвращать ничего, кроме как через его аргументы: C функции должны быть типа void, а подпрограммы на Фортране должны быть подпрограммами.

Вот несколько примеров, которые можно скомпилировать, как в OP. Похоже, что по умолчанию для возвращаемых функций .Call был тип "null", но allocVector(REALSXP, 0) R_NilValue, похоже, работал хорошо.

// test.cpp

#include <R.h>
#include <Rinternals.h>

extern "C" void fnPrintC() {
  Rprintf("Hello world!\n");
}

extern "C" SEXP fnPrintCall() {
  Rprintf("Hello world!\n");

  // return allocVector(REALSXP, 0);
  return R_NilValue;
}

extern "C" SEXP fnAddCall(SEXP a, SEXP b) {
  double* xa = REAL(a);
  double* xb = REAL(b);

  SEXP ans = allocVector(REALSXP, 2);
  REAL(ans)[0] = *xa + *xb;
  REAL(ans)[1] = *xa - *xb;

  return ans;
} 

Здесь они вызываются из R. Обратите внимание, что мы можем отправить вывод (void) в фиктивную переменную x, если мы не хотим его видеть.

> dyn.load("test.so")

> x <- .C("fnPrintC")
> Hello world!

> x <- .Call("fnPrintCall")
> Hello world!

> .Call("fnAddCall", 4, 3)
> [1] 7 1

В общем, документация связана выше, было очень полезно, я рекомендую начать там для любого с подобным вопросом, я, конечно, w sh я прочитал бы это более подробно ранее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...