如何防止Rcpp评估'call'对象

时间:2016-04-23 08:42:13

标签: r rcpp

我需要简单的包装器来从Rcpp代码中序列化任意R对象。下面是我的代码的简化版本:

// [[Rcpp::export]]
Rcpp::RawVector cpp_serialize(RObject x) {
  Rcpp::Function serialize = Rcpp::Environment::namespace_env("base")["serialize"];
  return serialize(x, R_NilValue);
}

这很好用,但我发现对于类call的对象,在序列化之前会调用该调用。我怎样才能防止这种情况发生?我只是想在R中模仿serialize()

# Works as intended
identical(serialize(iris, NULL), cpp_serialize(iris))

# Does not work: call is evaluated
call_object <- call("rnorm", 1000)
identical(serialize(call_object, NULL), cpp_serialize(call_object))

更新:我有一个解决方法(见下文),但我仍然对正确的解决方案非常感兴趣。

Rcpp::RawVector cpp_serialize(RObject x) {
  Rcpp::Environment env;
  env["MY_R_OBJECT"] = x;
  Rcpp::ExpressionVector expr("serialize(MY_R_OBJECT, NULL)");
  Rcpp::RawVector buf = Rcpp::Rcpp_eval(expr, env);
}

2 个答案:

答案 0 :(得分:3)

我认为您在Rcpp::Function课程中发现了意外行为。 MRE:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
RObject cpp_identity(RObject x) {
  Rcpp::Function identity("identity");
  return identity(x);
}

/*** R
quoted <- quote(print(1));
identity(quoted)
cpp_identity(quoted)
*/

给出

> quoted <- quote(print(1));

> identity(quoted)
print(1)

> cpp_identity(quoted)
[1] 1
[1] 1

这是因为Rcpp在幕后有效地执行了这项评估:

Rcpp_eval(Rf_lang2(Rf_install("identity"), x))

基本上就像

eval(call("identity", quoted))

但调用对象未受到评估的“保护”。

答案 1 :(得分:1)

tl; dr :问题是如何从C 序列化为Raw向量? RApiSerialization包中的(编译的C)函数serializeToRaw()提供了R自己的序列化代码。如下面的基准所示,它比上面建议的快三倍。

更长的答案:我不建议使用Rcpp::Function()来解决这个问题。我们实际上为R提供了一个适当的程序包来访问序列化:RApiSerialization。它没有做太多,但它只导出两个函数来序列化和反序列化RAW RcppRedis包需要和使用的Rcpp.package.skeleton()

所以我们可以在这里做同样的事情。我刚刚打电话给LinkingTo:打包了一个包&#39; jeroen&#39;已创建,将Imports:imports()添加到DESCRIPTION,将#include <Rcpp.h> #include <RApiSerializeAPI.h> // provides C API with serialization // [[Rcpp::export]] Rcpp::RawVector cpp_serialize(SEXP s) { Rcpp::RawVector x = serializeToRaw(s); // from RApiSerialize return x; } 添加到NAMESPACE,然后就可以了:

testJeroen <- function() {
    ## Works as intended
    res <- identical(serialize(iris, NULL), cpp_serialize(iris))

    ## Didn't work above, works now
    call_object <- call("rnorm", 1000)
    res <- res && 
           identical(serialize(call_object, NULL), cpp_serialize(call_object))

    res
}

它基本上是你上面的简单版本。

我们可以像你一样打电话给你:

R> library(jeroen)
Loading required package: RApiSerialize
R> testJeroen()
[1] TRUE
R> 

并且看,它有效:

Rcpp::Function()

简而言之:如果您不想使用R,请不要使用library(jeroen) # package containing both functions from here library(microbenchmark) microbenchmark(cpp=cpp_serialize(iris), # my suggestion env=env_serialize(iris)) # OP's suggestion, renamed 个对象。

基准测试:使用简单的

edd@max:/tmp/jeroen$ Rscript tests/quick.R 
Loading required package: RApiSerialize
Unit: microseconds
 expr    min      lq    mean  median      uq     max neval cld
  cpp 17.471 22.1225 28.0987 24.4975 26.4795 420.001   100  a 
  env 85.028 91.0055 94.8772 92.9465 94.9635 236.710   100   b
edd@max:/tmp/jeroen$ 

我们得到了

{{1}}

显示OP的答案几乎慢了三倍。