我正在开发Rcpp
中的一些函数,这些函数对big.matrix
包中的bigmemory
个对象进行操作。这些对象作为Rcpp
对象传递给SEXP
,然后我必须将其转换为XPtr<BigMatrix>
,然后转换为MatrixAccessor
对象以访问矩阵的元素。
例如,如果我想实现一个获得对角线的函数:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::depends(BH, bigmemory)
#include <bigmemory/MatrixAccessor.hpp>
#include <numeric>
// [[Rcpp::export]]
NumericVector GetDiag(SEXP pmat) {
XPtr<BigMatrix> xpMat(pMat); // allows you to access attributes
MatrixAccessor<double> mat(*xpMat); // allows you to access matrix elements
NumericVector diag(xpMat->nrow()); // Assume matrix is square
for (int i = 0; i < xpMat->nrow(); i++) {
diag[i] = mat[i][i];
}
return diag;
}
如果R中的big.matrix
对象填充了双精度数,则此函数可以很好地工作。
但是,如果在整数矩阵(例如diag(as.big.matrix(matrix(1:9, 3))@address)
)上调用此函数,则会导致垃圾,因为MatrixAccessor
已初始化为<double>
。
在内部,big.matrix
个对象可以有四种类型:
void typeof(SEXP pMat) {
XPtr<BigMatrix> xpMat(pMat);
int type = xpMat->matrix_type();
type == 1 // char
type == 2 // short
type == 4 // int
type == 8 // double
}
由于我们所做的只是访问矩阵的元素,diag
函数应该能够处理这些类型中的每一个。但是现在,由于我们的函数签名是NumericVector
,我将忽略字符矩阵。
为了解决这个问题,我想我可以抛出一个switch语句,在运行时使用适当的类型初始化相应的mat
:
// [[Rcpp::export]]
NumericVector GetDiag(SEXP pmat) {
XPtr<BigMatrix> xpMat(pMat);
// Determine the typeof(pmat), and initialize accordingly:
switch(xpMat->matrix_type()) {
case == 1:
{
// Function expects to return a NumericVector.
throw;
}
case == 2:
{
MatrixAccessor<short> mat(*xpMat);
break;
}
case == 4:
{
MatrixAccessor<int> mat(*xpMat);
break;
}
case == 8:
{
MatrixAccessor<double> mat(*xpMat);
}
}
MatrixAccessor<double> mat(*xpMat); // allows you to access matrix elements
NumericVector diag(xpMat->nrow()); // Assume matrix is square
for (int i = 0; i < xpMat->nrow(); i++) {
diag[i] = mat[i][i];
}
return diag;
}
但是,这会导致编译器错误,因为我已经在第一个mat
中声明了case
之后重新定义diag
。
我能看到这样做的唯一方法是编写三个不同的mat
函数,每个类型一个,其代码相同,但{{1}}的初始化除外。还有更好的方法吗?
答案 0 :(得分:8)
在这些情况下,您通常需要分离逻辑:首先,您有一个调度函数将SEXP
类型编组为某种编译时类型,以及一个单独的(模板化)函数来处理实际工作。一些(不完整的)示例代码:
#include <Rcpp.h>
using namespace Rcpp;
// the actual generic implementation
template <typename T>
T GetDiag_impl(T const& pMat) {
// logic goes here
}
// the dispatch code
// [[Rcpp::export]]
SEXP GetDiag(SEXP pMat) {
switch (TYPEOF(pMat)) {
case INTSXP: return GetDiag_impl<IntegerMatrix>(pMat);
case REALSXP: return GetDiag_impl<NumericMatrix>(pMat);
<...>
}
}
答案 1 :(得分:8)
感谢Kevin Ushey关于分离调度和功能逻辑的指示。所需的模板代码与Kevin的建议完全不同,以保证自己的答案。
要编写一个通常适用于所有类型的big.matrix
的函数,模式如下:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::depends(BH, bigmemory)]]
#include <bigmemory/MatrixAccessor.hpp>
// Generic Implementation of GetDiag:
template <typename T>
NumericVector GetDiag_impl(XPtr<BigMatrix> xpMat, MatrixAccessor<T> mat) {
NumericVector diag(xpMat->nrow()); // Assume matrix is square
for (unsigned int i = 0; i < xpMat->nrow(); i++) {
diag[i] = mat[i][i];
}
return diag;
}
// Dispatch code
// [[Rcpp::export]]
NumericVector GetDiag(SEXP pMat) {
XPtr<BigMatrix> xpMat(pMat);
switch(xpMat->matrix_type()) {
case 1:
return GetDiag_impl(xpMat, MatrixAccessor<char>(*xpMat));
case 2:
return GetDiag_impl(xpMat, MatrixAccessor<short>(*xpMat));
case 4:
return GetDiag_impl(xpMat, MatrixAccessor<int>(*xpMat));
case 8:
return GetDiag_impl(xpMat, MatrixAccessor<double>(*xpMat));
default:
// This should be impossible to reach, but shuts up the compiler
throw Rcpp::exception("Unknown type of big.matrix detected! Aborting.");
}
}
您无需模拟GetDiag_impl
的返回类型:所有四种big.matrix
类型都以R形式存储为数字(有关&#39; char&#的讨论,请参阅This Answer 39; big.matrix对象)。