Am using Rcpp packages and can get my C function to compile and run in R, but now I want to return a large, user-defined data structure to R. The fields in the structure are either numbers or strings - no new or odd types within the structure. The example below is simplified and doesn't compile, but it conveys the idea of my problem.
typedef struct {
char* firstname[128];
char* lastname[128];
int nbrOfSamples;
} HEADER_INFO;
// [[Rcpp::export]]
HEADER_INFO* read_header(Rcpp::StringVector strings) {
FILE *fp;
MEF_HEADER_INFO *header;
char * filename = (char*)(strings(0));
char * password = (char*)(strings(1));
header = (HEADER_INFO*)malloc(sizeof(HEADER_INFO));
memset(header, 0, sizeof(HEADER_INFO));
fp = fopen(filename, "r");
(void)read_header(header, password);
return header;
}
I'm pretty sure that I could package the entries in the header back into a StringVector, but that seems like a brute-force approach. My question is whether a more elegant solution exists. It is not clear to me what form such a structure would even have in R: a named List?
Thanks!
The 'Rcpp' package provides R functions as well as C++ classes which offer a seamless integration of R and C++. Many R data types and objects can be mapped back and forth to C++ equivalents which facilitates both writing of new code as well as easier integration of third-party libraries.
R treats C++ classed like an S4 class. Under the hood, a pointer to a C++ object instance is passed around, so you can create an object instance in C++ and then pass it to R sharing data.
The right structure in R depends on what your struct
looks like exactly. A named list is the most general one. Here a simple sample implementation for a wrap
function as referred to in the comments:
#include <RcppCommon.h>
typedef struct {
char* firstname[128];
char* lastname[128];
int nbrOfSamples;
} HEADER_INFO;
namespace Rcpp {
template <>
SEXP wrap(const HEADER_INFO& x);
}
#include <Rcpp.h>
namespace Rcpp {
template <>
SEXP wrap(const HEADER_INFO& x) {
Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = firstname,
Rcpp::Named("lastname") = lastname,
Rcpp::Named("nbrOfSamples") = Rcpp::wrap(x.nbrOfSamples)));
};
}
// [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
HEADER_INFO header;
header.firstname[0] = (char*)"Albert";
header.lastname[0] = (char*)"Einstein";
header.firstname[1] = (char*)"Niels";
header.lastname[1] = (char*)"Bohr";
header.firstname[2] = (char*)"Werner";
header.lastname[2] = (char*)"Heisenberg";
header.nbrOfSamples = 3;
return header;
}
/*** R
getHeaderInfo()
*/
Output:
> getHeaderInfo()
$firstname
[1] "Albert" "Niels" "Werner"
$lastname
[1] "Einstein" "Bohr" "Heisenberg"
$nbrOfSamples
[1] 3
However, for this particular case a data.frame
would be more natural to use, which can be achieved by replacing above wrap
with:
template <>
SEXP wrap(const HEADER_INFO& x) {
Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
return Rcpp::wrap(Rcpp::DataFrame::create(Rcpp::Named("firstname") = firstname,
Rcpp::Named("lastname") = lastname));
};
Output:
> getHeaderInfo()
firstname lastname
1 Albert Einstein
2 Niels Bohr
3 Werner Heisenberg
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With