I have a simulation model which is written as a C++ class. I have written a simple Rcpp
wrapper to allow it to be run from R
. I am wondering about the best way to get and set the model properties (doubles, vectors of doubles, and vectors of vectors of doubles) from R
. I am using three std::unordered_map
as a way of allowing the user to access the model properties:
class molly_class {
...
public:
// unordered_map gives user efficient access to variables by name
std::unordered_map< std::string , double > variable;
std::unordered_map< std::string , std::vector< double >* > vector; // pointers to vectors
std::unordered_map< std::string , std::vector< std::vector< double > >* > array; // pointers to arrays
...
}
In my Rcpp
wrapper I have provided methods to access the "variables" (scalars), "vectors" and "arrays" from R
. It works, but I unsure whether this is the right way to go. I want to avoid unnecessary copying of information, and maybe there are more efficient/safer ways of passing the data back and forth? I looked in the Rcpp
documentation about exposing C++ classes and RCCP_MODULE
but I don't really understand it. Thanks in advance. The Rcpp
wrapper contains the following:
molly_class molly;
// return molly scalar variables and their values as a named vector
// [[Rcpp::export]]
Rcpp::NumericVector get_molly_variables(){
Rcpp::NumericVector var_vector;
molly.pull_variables_from_model();
var_vector = molly.variable; // coerces unordered_map to named vector
return var_vector;
}
// set values of molly variables from the supplied named vector
// [[Rcpp::export]]
void set_molly_variables(Rcpp::NumericVector var_vector){
Rcpp::CharacterVector names = var_vector.names(); // types are a bit tricky
std::string name_i;
molly.pull_variables_from_model();
for (int i=0; i<var_vector.size(); ++i){
name_i = Rcpp::as<std::string>(names[i]); // types are a bit tricky
if (molly.variable.find(name_i) != molly.variable.end()){
molly.variable[name_i] = var_vector[i];
} else {
Rcpp::Rcout << "molly error : unknown variable " << name_i << std::endl;
}
}
molly.push_variables_to_model();
}
// return names of molly vectors
// [[Rcpp::export]]
Rcpp::StringVector get_molly_vectors(){
Rcpp::StringVector key_vector;
// https://stackoverflow.com/questions/8483985/obtaining-list-of-keys-and-values-from-unordered-map
for (auto mvi : molly.vector){
key_vector.push_back(mvi.first);
}
return key_vector;
}
// return a molly vector
// [[Rcpp::export]]
Rcpp::NumericVector get_molly_vector(Rcpp::String vec_name){
Rcpp::NumericVector vec_values;
vec_values = *molly.vector[vec_name];
return vec_values;
}
// set a molly vector using the supplied vector
// [[Rcpp::export]]
void set_molly_vector(Rcpp::String vec_name, Rcpp::NumericVector vec_values){
int ncols = (*molly.vector[vec_name]).size();
if (vec_values.size() == ncols){
for (int col=0; col<ncols; col++){
(*molly.vector[vec_name]).at(col) = vec_values.at(col);
}
} else {
Rcpp::Rcout << "molly error : supplied vector does not match length of " << vec_name << std::endl;
}
}
// return names of molly arrays
// [[Rcpp::export]]
Rcpp::StringVector get_molly_arrays(){
Rcpp::StringVector key_vector;
// https://stackoverflow.com/questions/8483985/obtaining-list-of-keys-and-values-from-unordered-map
// key_vector.reserve(molly.vector.size());
for (auto mvi : molly.array){
key_vector.push_back(mvi.first);
}
return key_vector;
}
// return a molly array as a matrix
// [[Rcpp::export]]
Rcpp::NumericMatrix get_molly_array(Rcpp::String arr_name){
int nrows = (*molly.array[arr_name]).size();
int ncols = (*molly.array[arr_name]).at(0).size();
Rcpp::NumericMatrix arr_values(nrows, ncols);
for (int row=0; row<nrows; row++){
for (int col=0; col<ncols; col++){
arr_values(row, col) = (*molly.array[arr_name]).at(row).at(col);
}
}
return arr_values;
}
// set a molly array using the supplied matrix
// [[Rcpp::export]]
void set_molly_array(Rcpp::String arr_name, Rcpp::NumericMatrix arr_values){
int nrows = (*molly.array[arr_name]).size();
int ncols = (*molly.array[arr_name]).at(0).size();
if (arr_values.nrow() == nrows && arr_values.ncol() == ncols){
for (int row=0; row<nrows; row++){
for (int col=0; col<ncols; col++){
(*molly.array[arr_name]).at(row).at(col) = arr_values(row, col);
}
}
} else {
Rcpp::Rcout << "molly error : supplied matrix does not match dimensions of " << arr_name << std::endl;
}
Note that pull_variables_from_model()
populates the unordered_map
variable
and push_variables_to_model()
uses the unordered_map
variable
to set the internal model variables, which are private members of molly_class
. The unordered_map
variable
is only used as an interface to allow users to access the internal model variables by name.