Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

rcpp - Initialize a variable with different type based on a switch statement

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

This function works beautifully, provided the big.matrix object in R is filled with doubles.

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>.

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
}

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.

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

However, this results in compiler errors, because I'm redefining mat after it has been declared already in the first case.

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In these cases, you usually want to separate the logic: first, you have a dispatch function that marshalls the SEXP type into some compile-time type, and a separate (templated) function that handles the real work. Some (incomplete) example code:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...