4

I have a cppFunction with a vector ints as input, e.g:

library(Rcpp)
cppFunction('double test2(NumericVector ints) {
            return 42;
            }')

The output is correct if passing a vector of length at least 1:

> test2(1)
[1] 42
> test2(1:10)
[1] 42

For input of length 0 however I get:

> test2(c())
Error: not compatible with requested type

Is there any way to pass a vector of length 0 or larger to my function? I.e. my expected output is:

> test2_expectedoutput(c())
[1] 42

I know I could control for this in R by checking in R first and calling a different version of the function but would like to avoid this. I expect there is some easy solution out there since within cpp I could also have a NumericVector of length 0 if I understand correctly what NumericVector zero; does. The only related question I could find was this on how to return a NULL object from within a Rcpp function to R.

Community
  • 1
  • 1
mts
  • 2,160
  • 2
  • 24
  • 34

2 Answers2

9

A few months ago we added the ability to pass as Nullable<T> which may be what you want here.

Here is a simple example:

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
bool checkNull(Nullable<NumericVector> x) {
  if (x.isNotNull()) {
    // do something
    NumericVector xx(x);
    Rcpp::Rcout << "Sum is " << sum(xx) << std::endl;
    return true;
  } else {
    // do nothing
    Rcpp::Rcout << "Nothing to see" << std::endl;
    return false;
  }
}

/*** R
checkNull(1:3)
checkNull(NULL)
*/

and its output:

R> sourceCpp("/tmp/null.cpp")

R> checkNull(1:3)
Sum is 6
[1] TRUE

R> checkNull(NULL)
Nothing to see
[1] FALSE
R> 

By being templated we respect the intended type but clearly differentiate between being there, and not.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • 1
    Well not to toot my horn to much but it encompasses his answers as you also get all the sanity checks he recommends behind the scenes. And `NULL` is more explicit than length 0. – Dirk Eddelbuettel Jan 11 '16 at 14:58
  • I accepted nicolas answer because it was first and answered my question (before this answer was posted). It provides a quick and simple workaround that works nicely for me while this answer shows how to deal with my problem within Rcpp in a much more general way. As a noob to Rcpp I learned a great deal from both answers and believe they are complementary. Let me delete my previous comment to avoid misunderstandings and thanks again! +1 – mts Jan 11 '16 at 15:37
  • 2
    (I am not suggesting/requesting you do this but ...) people switch accepted votes all the time. The point of SO is to have the best solution bubble up. Pick the answer your find best responds to your question. That's how the merit system is supposed to work. – Dirk Eddelbuettel Jan 11 '16 at 15:48
4

The c() calls produces NULL which is not a numeric vector. This generates the error when test2 is called. You can build a numeric vector of length 0 through numeric:

#check what `c()` does
str(c())
# NULL

# now we try numeric(0)
test2(numeric(0))
#[1] 42

As a suggestion, I think that C, Fortran or C++ functions should rarely be called directly; much better to write a wrapper that does some preliminary operations, like type conversions and similar. Something like the following:

test2Wrapp<-function(x) test2(as.numeric(x))
test2Wrapp(c())
#[1] 42
#This has the benefit to not calling the internal routines in cases where conversion isn't possible
test2Wrapp(iris)
#Error: (list) object cannot be coerced to type 'double'
nicola
  • 24,005
  • 3
  • 35
  • 56
  • 1
    Inputs of `raw()`, `logical()`, `integer()`, `double()`, `complex()`, and `vector()` will work here, too. – Jota Jan 11 '16 at 10:33
  • 1
    Yes, this is due to the specific `Rcpp` interface and what it accepts as `NumericVector`. Apparently, `logical`, `raw` and `complex` all qualify. – nicola Jan 11 '16 at 10:36