基于switch语句初始化不同类型的变量 [英] Initialize a variable with different type based on a switch statement

查看:42
本文介绍了基于switch语句初始化不同类型的变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆