2016-04-23 35 views
4

我需要简单的包装来从Rcpp代码中序列化任意R对象。下面我的代码的简化版本:如何防止Rcpp评估“调用”对象

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

这个伟大的工程,但我发现,call类对象的呼叫会被序列化之前评估。我怎样才能防止这种情况发生?我只是想模仿serialize()在R.

# 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); 
} 
+0

为'call'创建'call',然后可能只是顶部的'call'将被评估?只是一个随机的想法,我没有真正尝试过。 –

+0

解决方法也在我脑海中。即使在旧的JSS论文中,我们也会展示三种不同的方式来达到'rnorm()',在这里你找到了两个。整个方法仍然不是我想做的 - 看到我现在添加的答案。 –

+0

您不需要解决方法。你想序列化到C级的原始对象,并且有一个包给你'serializeToRaw()'作为本地C函数_。我的回答如下,downvoted两次(!!),使用它。 –

回答

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

TL;博士:问题是一个人怎么连载从C原始载体?提供R自己的序列化代码的RApiSerialization包中的(编译的C)函数serializeToRaw()。如下面的基准所示,它比上面提出的要快三倍。

再回应:我不建议用Rcpp::Function()这个碴左右。我们实际上提供对于R的接入系列化适当包装:RApiSerialization。它并没有太多的功能,但它只出口两个函数,用于序列化并反序列化RAW,其中RcppRedis包需要和使用。

所以我们可以在这里做同样的事情。我只是叫Rcpp.package.skeleton()有创建包“吉荣”,增加了LinkingTo:Imports:来描述和imports()到命名空间,然后这个工程:

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

它基本上是你有什么上述简化版本。

而且我们可以调用你做到:

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> 

因此,在短期:如果你不想有R渣土,不要与Rcpp::Function()对象一起使用。

基准:使用一个简单的

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 

我们得到

[email protected]:/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 
[email protected]:/tmp/jeroen$ 

表明通过OP答案是近三倍慢。