передача невычисленных выражений в C/C++

Я хотел бы передать переменное количество аргументов из функции в C/C++, но хотел бы оставить аргументы невычисленными и в то же время не хочу выполнять какие-либо вычисления в R (кроме вызова C/C++ функция), то есть я не хочу вызывать substitute в моей функции R. Один из вариантов, который, как я думал, я мог бы использовать, - это .External и сделать что-то вроде этого:

R_fn = function(...) .External("cpp_fn", ...)

...
# and in C code:
SEXP cpp_fn (SEXP arglist) {
}

Однако .External оценивает аргументы в ..., поэтому, если я попробую что-то вроде

rm(x, y) # just making sure these don't exist

R_fn(x*y)

Я получаю сообщение об ошибке, потому что R пытается оценить x*y прежде чем отправить его в функцию.

Для сравнения, в R работает следующее:

f = function(...) g(...)
g = function(x, ...) print(substitute(x))

f(x*y*z)
# x * y * z

Какие еще варианты у меня есть? Очевидно, что это можно сделать так, как это делает сам R для ряда функций, например. substitute сам, но я не понимаю, как это сделать. Я добавил тег rcpp, потому что в конечном итоге я буду использовать его в Rcpp.


c r rcpp
person eddi    schedule 30.10.2013    source источник
comment
Я бы никогда не подумал, что есть простой способ сделать это без substitute, учитывая, для чего он предназначен? Но тогда мои познания в R-внутренностях довольно начальные. Есть ли рабочая причина, по которой вы не можете использовать substitute? Это удобно решает проблему, как в следующем примере: require( inline ); cdr <- cfunction(c(x = "ANY"), 'return(CDR(x));'); cdr( substitute( x * y ) );cdr( x * y )   -  person Simon O'Hanlon    schedule 30.10.2013
comment
@SimonO101 спасибо, но я не хочу использовать substitute, потому что я хочу абсолютно минимизировать время, затрачиваемое на различные отправки R, и эти вызовы в конечном итоге обходятся мне дорого.   -  person eddi    schedule 30.10.2013
comment
Ok. Я предлагаю посмотреть, как R обрабатывает $, чьи аргументы никогда не оцениваются, а также [[ (оба в src/main/subset.c). В нем конкретно указано: /* The [[ subset operator. It needs to be fast. */. Может быть, там есть какие-то указатели.   -  person Simon O'Hanlon    schedule 30.10.2013
comment
Я фиксирую невычисленное выражение в R (github.com/hadley/pryr/blob /master/R/inspect.r), а затем использовать его для поиска var в C++ (github.com/hadley/pryr/blob/master/src/inspect.cpp)   -  person hadley    schedule 30.10.2013
comment
Да, и поклон @hadley за функцию cdr выше из Расширенный R   -  person Simon O'Hanlon    schedule 30.10.2013
comment
Я бы проголосовал за удаление тега Rcpp. Здесь все происходит за пределами .Call().   -  person Dirk Eddelbuettel    schedule 30.10.2013
comment
@SimonO101 Дело в том, что все эти функции используют .Primitive для взаимодействия с C, и я не думаю, что это доступно для меня (не так ли?)   -  person eddi    schedule 30.10.2013
comment
спасибо @hadley, я понимаю, что могу substitute сначала, но я бы очень хотел избежать лишних вычислений в R   -  person eddi    schedule 30.10.2013
comment
@eddi Насколько мне известно, других вариантов в настоящее время нет. Это не так много работы.   -  person hadley    schedule 30.10.2013
comment
@hadley, но функции R делают это - substitute, ||, && и т. д. все передают свои аргументы в C без оценки - что в них такого особенного? (может быть, это еще один способ сказать, что я не понимаю, как работает .Primitive); может показаться, что это не так уж и много работы, но когда ваша функция C++ вызывает вашу собственную функцию R в цикле, это довольно много.   -  person eddi    schedule 30.10.2013
comment
Они особенные, потому что они являются примитивными/внутренними функциями и могут делать то, что вы не можете делать с .Call/.External.   -  person hadley    schedule 30.10.2013
comment
@DirkEddelbuettel, кстати, какому типу Rcpp соответствует результат substitute? Или универсального ответа на этот вопрос нет?   -  person eddi    schedule 31.10.2013
comment
Я так не думаю, и теперь я удалил тег rcpp.   -  person Dirk Eddelbuettel    schedule 31.10.2013
comment
@eddi Это либо атомарный вектор (REALSXP/INTSXP/LGLSXP/и т. д.) длины 1, имя (SYMSXP) или вызов (LANGSXP). См. adv-r.had.co.nz/Expressions.html.   -  person hadley    schedule 31.10.2013


Ответы (1)


Один из вариантов — сделать то, что делает match.call (спасибо Рикардо Сапорте за то, что указал мне в этом направлении). Это требует копирования и вставки нескольких определений из исходного кода R, которые я не буду здесь делать, но основная идея состоит в том, чтобы получить вызывающую функцию из R_GlobalContext, а затем извлечь оттуда аргументы функции. Грубый набросок выглядит следующим образом:

R_fn = function(...) .Call("cpp_fn")

// and in C++ code
Language cpp_fn() {
  SEXP sysp = ((RCNTXT*)R_GlobalContext)->sysparent;
  RCNTXT *cptr = (RCNTXT*)R_GlobalContext;

  while (cptr != NULL) {
    if (cptr->callflag & CTXT_FUNCTION && cptr->cloenv == sysp)
      break;
    cptr = cptr->nextcontext;
  }
  cptr = cptr->nextcontext; // because this is called from .Call and not from R_fn

  // and now cptr->promargs has the unevaluated arguments to do as one pleases
  // e.g.
  Language firstArg(R_PromiseExpr(CAR(cptr->promargs)));

  return firstArg;
}
person eddi    schedule 31.10.2013
comment
+1 крутая штука. Можете ли вы объяснить, почему вы должны сделать sysp = ((RCNTXT*)R_GlobalContext)->sysparent, а не просто sysp = R_GlobalContext->sysparent? И какой прирост производительности вы увидели при использовании этого метода по сравнению с заменой для ваших нужд? - person Simon O'Hanlon; 01.11.2013
comment
@SimonO101, потому что R_GlobalContext экспортируется как void *, и здесь вступает в действие копирование и вставка, поскольку RCNTXT не экспортируется, поэтому вам нужно скопировать и вставить эту структуру. Я еще не использовал это в реальной жизни, так как изучаю различные варианты, если / когда я это сделаю, это будет в data.table. - person eddi; 01.11.2013
comment
Спасибо за дополнительное объяснение, Эдди. Это отличный материал. Я буду следить за исходниками dt (в любом случае это уже блестящий пакет). - person Simon O'Hanlon; 01.11.2013
comment
Использование неэкспортированного c api вызывает проблемы. - person hadley; 03.11.2013
comment
Эдди, ты этим воспользовался? У меня очень вопрос, похожий на ваш< /b>, но надеялся, что есть более законный способ сделать что-то подобное. - person BrodieG; 19.09.2014
comment
@BrodieG Я этого не делал, но не вижу причин считать это незаконным - пока указатель продолжает экспортироваться, вы всегда можете изменить свой код, чтобы он соответствовал структуре, если она изменится в будущем. - person eddi; 19.09.2014
comment
Что ты в итоге скопировал? Я пытался получить материал из Defn.h, но не смог скомпилировать свой код и, похоже, не смог найти все необходимые определения (застрял в определении JMP_BUF). - person BrodieG; 20.09.2014
comment
@BrodieG Я не помню, что еще мне пришлось копировать, кроме RCNTXT - предположительно также определения JMP_BUF сверху (в том же файле) и соответствующие включения (поиск R_USE_SIGNALS в этом файле будет охватывать вышеуказанные пункты) - person eddi; 20.09.2014