Подтвердить что ты не робот

Передача неоцененных выражений в C/С++

Я бы хотел передать переменное количество аргументов из функции в C/С++, но хотел бы оставить аргументы необоснованными и в то же время не делать никаких вычислений в R (кроме вызова 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.

4b9b3361

Ответ 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;
}