从NumericVector填充C ++数组的更好的习惯用法

时间:2012-12-24 13:48:45

标签: c++ r rcpp

我正在使用Rcpp来包装一个在C语言C ++中编写的算法(不是我)(没有STL,没有提升,没有任何东西,据我所知)。你可以看到实现的算法here(我正在包装kmeans_w_03)。因此,我从R传入numeric向量,然后需要将其转换为double数组。

目前我正在循环逐个元素并从“tother”中填充一个,就像这样:

SEXP testfn(SEXP weightvec, SEXP cluster_num_k){
    Rcpp::NumericVector weightR(weightvec) ;
    int point_num = weightR.size();
    double weight[point_num] ;
    for(int i = 0; i < point_num; ++i) {
      weight[i] = weightR[i];
    }
}

但是使用单元素数字向量,我可以利用Rcpp漂亮的as强制转换功能:

int cluster_num = Rcpp::as<int>(cluster_num_k);

尝试类似长度的东西&gt;但是,1个数字向量会导致崩溃或错误,具体取决于语法的确切变体:

double weight[point_num] = Rcpp::as<double>(weightvec);

我不一定介意循环,但我是一个完全新手,并怀疑有更好的方法。我已经阅读了Rcpp介绍,hadley的wiki教程和RcppExamples,但还没有找到解决这个问题的任何内容,但这并不意味着我不会错过它。我读到的Doxygen Rcpp文档是as可以转换为STL向量而不是数组(但我很难读取这些文档,所以我怀疑我错了)。如果是这样,我想我可以投射到一个矢量,然后再投射到数组......

所以我的问题是:是否有更好的(更少的代码行,更具表现力的代码,甚至可以避免内存分配)方式将NumericVector转换为{{1 }}?

3 个答案:

答案 0 :(得分:8)

Ari,在之前的回答中,John提出了一个很好的观点。为了扩展一点,这里有一些问题

  • 您正在处理一个过时的编码标准库,该标准需要double[]*double
  • 您希望使用更好的编码标准并利用Rcpp

好吧,不要害怕,我们有一个简单的解决方案。像往常一样立即对齐Rcpp::NumericVector X(weightvec);,然后将其传递给您的函数foo()(或其他)

foo(X.begin())

为您提供所需的double*,如果需要X.size()则提供长度。因为你来自R,所以当你之后返回R时,你不需要担心范围和生命周期。

如果你需要更明确,我也使用了丑陋的&(X[0])

此外,Rcpp::as<>()施法者也适用于std::vector<double>,所以你可以做到

std::vector<double> x = Rcpp::as<std::vector<double> >(weightvec);

但除了转换为C ++类型之外,它在这里没有任何收获。

最后,如果Josh或其他C顽固分子在附近,所有这些都使用了这样一个事实:来自R的SEXP保证有连续的C指针,所以你也可以通过这个非常古老的学校{ {1}}。

答案 1 :(得分:4)

编写一个不使用标准库(你称之为“STL”)的C ++程序并不是我认为的“常规C ++”。标准库是 标准所定义语言的一部分。因此,使用标准中定义的工具不是非常规的C ++。恰恰相反,我建议故意忽视这些设施本身就是非传统的。它使用C ++作为“更好的C”,或者“C with classes”。而那就错过了重点。

考虑到这一点,在查看您的循环打算完成的内容时,我的第一个倾向是使用transform将源复制到目标,并将vector作为目标。

您还有一位中间人在这里工作 - 您首先(似乎)将weightVec复制到新构建的NumericVector,然后复制其中的元素 - 但{{{ 1}}被扔掉了。这对我来说似乎很浪费。

以下是编写此代码时我想到的基本模式:

NumericVector

这里缺少一些语法位,如果不了解更多关于组件的内容,我无法填写。但鉴于你正在做的事情,哲学似乎是合理的。

例如,vector<double> weight; transform( weightVec.begin(), weightVec.end(), back_inserter(weight), converter() ); return weight; 对象可能没有SEXPbegin()方法。但是,您已经直接从end()对象构建NumericVector,因此SEXP可能具有SEXPbegin()的模拟工具,您可以采用或可能甚至直接使用。

我上面使用的end()构造是函数,函子或lambda(在C ++ 11中)的占位符,它将单个converter()元素转换为SEXP。我再次不知道为了实现这一点我需要的细节,但它必须是可能的,因为你已经以另一种方式做到了这一点。

答案 2 :(得分:1)

如果您的问题是如何将数据从Rcpp::NumericVector复制到C ++数组,那么transform()就是一个很好的答案。但是如果你想从Rcpp::NumericVector获取内部数组(不包括任何复制),你可以使用std::vector中的提取数组完全相同:

double* arr = &vec[0];

其中vec是Rcpp::NumericVector