Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize a variable with different type based on a switch statement

Tags:

rcpp

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?

like image 765
Scott Ritchie Avatar asked Jul 08 '14 02:07

Scott Ritchie


1 Answers

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);
  <...>
  }
}
like image 115
Kevin Ushey Avatar answered Oct 12 '22 16:10

Kevin Ushey