Reputation: 351
I wrote the following Rcpp code which compiles but does not give the expected result.
// [[Rcpp::export]]
RObject test_1 (Rcpp::NumericVector& x)
{
NumericVector x1;
if (x.size() < 5)
{
NumericVector x1(x.size()*3);
for (int ii = 0; ii < x.size(); ii++)
{
x1[ii] = sqrt(x[ii]);
}
}
else
{
NumericVector x1(x.size()*2);
for (int ii = 0; ii < x.size(); ii++)
{
x1[ii] = sqrt(x[ii]);
}
}
return x1;
}
x1, the return vector is declared outside the if statement where the size of the returned vector x1 can vary. But if I declare the NumericVector with the size it created a new object, but the x1 that is returned in an empty vector.
The following code works:
// [[Rcpp::export]]
RObject test (const Rcpp::NumericVector& x)
{
NumericVector x1;
if (x.size() < 5)
{
NumericVector tmp(x.size()*3);
for (int ii = 0; ii < x.size(); ii++)
{
tmp[ii] = sqrt(x[ii]);
}
x1 = tmp;
}
else
{
NumericVector tmp(x.size()*2);
for (int ii = 0; ii < x.size(); ii++)
{
tmp[ii] = sqrt(x[ii]);
}
x1 = tmp;
}
return x1;
}
Here I declare temp numericvector and then set x1 to that vector.
I did this a while ago and know that there is a way to set a NumericVector to a specific size after it has been declared, I just cannot remember how.
Edit: Edited the code and question to show how the return vector size is not known before the if statement.
Upvotes: 2
Views: 6637
Reputation: 21285
You could use this style:
NumericVector x;
if (foo)
x = NumericVector(1);
else
x = NumericVector(2);
Or even (if you want to avoid zero initialization of the generated vector)
x = static_cast<NumericVector>(no_init(size));
Note that when you write
NumericVector x;
if (foo)
NumericVector x(1);
you are actually creating two NumericVector
objects called x
-- one living at the top-level scope, and one living within the scope of the if
statement.
That is, it's worth understanding that when you write
NumericVector x;
you are actually creating an object, not just declaring an object (that is only true for built-in types, e.g. int
). So, when you write NumericVector x
, x
is actually default-constructed with the zero-argument NumericVector
constructor (which creates a numeric vector of length 0)
Upvotes: 5
Reputation: 18602
You could conditionally resize
a std::vector<double>
and Rcpp::wrap()
it, like this:
#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::RObject test_1(const Rcpp::NumericVector& x) {
std::vector<double> x1;
if (x.size() < 5) {
x1.resize(3*x.size());
} else {
x1.resize(2*x.size());
}
for (int ii = 0; ii < x.size(); ii++) {
x1[ii] = sqrt(x[ii]);
}
return Rcpp::wrap(x1);
}
/*** R
xa <- 1:3
xb <- 1:10
test_1(xa)
# [1] 1.000000 1.414214 1.732051 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
test_1(xb)
#[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 3.000000 3.162278 0.000000 0.000000
[13] 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
length(test_1(xa))/length(xa)
#[1] 3
length(test_1(xb))/length(xb)
#[1] 2
*/
Upvotes: 2
Reputation: 1212
you can do something like this
RObject test_1 (Rcpp::NumericVector x)
{
for (int ii = 0; ii < x.size(); ii++)
{
x[ii] = sqrt(x[ii]);
}
return x;
}
By passing by value you create a new NumericVector
of the same size of the original one
if you don't want to change your API, the second code is OK but the temp var is not necessary
RObject test_1 (const Rcpp::NumericVector& x)
{
NumericVector x1(x.size());
for (int ii = 0; ii < x.size(); ii++)
{
x1[ii] = sqrt(x[ii]);
}
return x1;
}
Upvotes: 1