基于switch语句初始化不同类型的变量 [英] Initialize a variable with different type based on a switch statement
问题描述
我正在 Rcpp
中开发一些函数,这些函数对 bigmemory
包中的 big.matrix
对象进行操作.这些对象被传递给 Rcpp
作为 SEXP
对象,然后我必须将它们转换为 XPtr
,然后转换为 MatrixAccessor
对象来访问矩阵的元素.
I am developing some functions in Rcpp
that operate on big.matrix
objects from the bigmemory
package. These objects are passed to Rcpp
as SEXP
objects which i then have to cast to an XPtr<BigMatrix>
, and then to a MatrixAccessor
object to access elements of the matrix.
例如,如果我想实现一个获取对角线的函数:
For example, if I want to implement a function that get's the diagonal:
#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
对象用双精度填充,则此函数工作得很好.
This function works beautifully, provided the big.matrix
object in R is filled with doubles.
然而,如果你在一个整数矩阵上调用这个函数(例如 diag(as.big.matrix(matrix(1:9, 3))@address)
),你会得到垃圾作为结果,因为 MatrixAccessor
已被初始化为
.
However, if you call this function on an integer matrix (e.g. diag(as.big.matrix(matrix(1:9, 3))@address)
), You get garbage as a result, because the MatrixAccessor
has been initialized as <double>
.
在内部,big.matrix
对象可以有四种类型:
Internally, big.matrix
objects can come in four types:
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
,我将忽略字符矩阵.
Since all we're doing is accessing the elements of the matrix, the diag
function should be able to handle each of these types. But for now, since our function signature is NumericVector
, I'll ignore character matrices.
为了解决这个问题,我想我可以只抛出一个 switch 语句,在运行时用适当的类型初始化相应的 mat
:
To handle this, I figured I could just throw in a switch statement, initializing the corresponding mat
with the appropriate type at runtime:
// [[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
中声明之后重新定义它.
However, this results in compiler errors, because I'm redefining mat
after it has been declared already in the first case
.
我能看到的唯一方法是编写三个不同的 diag
函数,每个类型一个,除了 mat<的初始化之外,它们的代码是相同的/代码>.有没有更好的办法?
The only way I can see to do this, is to write three different diag
functions, one for each type, whose code is the same with the exception of the initialization of mat
. Is there a better way?
推荐答案
感谢 Kevin Ushey 提出的分离调度和函数逻辑的建议.所需的模板代码与 Kevin 的建议大不相同,以保证其自己的答案.
Thanks to the pointers from Kevin Ushey on separating the dispatching and the function logic. The template code required is sufficiently different to Kevin's suggestions to warrant its own answer.
写一个函数,一般适用于所有类型的big.matrix
,模式如下:
To write a function that generally works on all types of big.matrix
, the pattern is as follows:
#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 中(请参阅 这个答案 用于讨论'char' big.matrix 对象.
You do not need to template the return type of GetDiag_impl
: all four big.matrix
types are stored as numeric in R (See This Answer for a discussion on 'char' big.matrix objects).
这篇关于基于switch语句初始化不同类型的变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!