Search code examples
c++rrcppstdstringr-package

How to work with Rcpp strings variables which could be NULL?


I am writing an R package + Rcpp code to work with an existing C++ library.

After going through the tutorials here: https://gallery.rcpp.org/articles/optional-null-function-arguments/ , I'm struggling with how to work with NULL and strings. I am confused that I cannot cast from type Rcpp::Nullable<std::string> to std::string (or equivalently Rcpp::Nullable<Rcpp::String> to Rcpp::String

Within C++, I am checking for whether the string (in C++) is empty or not. If the string is empty, I want to return NULL. If the string (in C++) is not empty, I want to return the string.

My example code is below, modifying the function rcpp_hello_world() provided by the Rcpp.package.skeleton() for simplicity. My goal is to return within R a list (Rcpp::List) containing either a string, or NULL (if the string is empty).

#include <Rcpp.h>
#include <string>
using namespace Rcpp;

// [[Rcpp::export]]
Rcpp::List rcpp_hello_world() {

    // After calculations from external C++ library,
    // the variable 'mystring' will either empty (i.e. "") or populated (e.g. "helloworld")

    std::string mystring = "helloworld";  // string non-empty

    Rcpp::Nullable<std::string> result_string = R_NilValue;
    
    if (!mystring.empty()) {
        std::string result_string(mystring);
    }

    Rcpp::List z = List::create(result_string);

    return z ;
}

The resulting variable result_string in the above example should either be NULL or "mystring"---however, the above will always return NULL, which is not the desired behavior.

I then tried to see if I could even convert types between Rcpp::Nullable<std::string> and std::string:

std::string mystring = "helloworld";  
Rcpp::Nullable<std::string> result_string = R_NilValue;
std::string result_string(mystring);

This results in a compilation error:

error: redefinition of 'result_string' with a different type: 'std::string' 
(aka 'basic_string<char, char_traits<char>, allocator<char>>') vs 'Rcpp::Nullable<std::string>' 
(aka 'Nullable<basic_string<char, char_traits<char>, allocator<char>>>')

Am I using the wrong data structures for this operation? Or is there a better way to work with strings if the value could be NULL?


Solution

  • Here is a minimally complete answer. In the function body you can adjust your tests according to your needs, this is just a placeholder example.

    Code

    #include <Rcpp.h>
    
    // [[Rcpp::export]]
    Rcpp::List foo(Rcpp::NumericVector v) {
    
        // we just us a random vector here to determine: if positive
        // we inject a string, if negative NULL
    
        const std::string mystring = "helloworld";  // if positive
    
        int n = v.size();
        Rcpp::List z(n);
    
        for (int i=0; i<n; i++) {
            if (v[i] < 0) {
                z[i] = mystring;
            } else {
                z[i] = R_NilValue;
            }
        }
    
        return z;
    }
    
    /*** R
    set.seed(123)
    foo(rnorm(3)))
    set.seed(123456)
    foo(rnorm(3))
    */
    

    Output

    > Rcpp::sourceCpp("~/git/stackoverflow/70601602/answer.cpp")
    
    > set.seed(123)
    
    > foo(rnorm(3))
    [[1]]
    [1] "helloworld"
    
    [[2]]
    [1] "helloworld"
    
    [[3]]
    NULL
    
    
    > set.seed(123456)
    
    > foo(rnorm(3))
    [[1]]
    NULL
    
    [[2]]
    [1] "helloworld"
    
    [[3]]
    [1] "helloworld"
    
    >