16

I am trying to write some simple Rcpp code examples. This is remarkably easy with the Rcpp and inline packages.

But I am stumped on how to test whether two character elements for equality. The following example compares the first elements of two character vectors. But I can't get it to compile.

What is the trick?

library(Rcpp)
library(inline)

cCode <- '
    Rcpp::CharacterVector cx(x);
    Rcpp::CharacterVector cy(y);
    Rcpp::LogicalVector r(1);
    r[0] = (cx[0] == cy[0]);
    return(r);
    '

cCharCompare <- cxxfunction(signature(x="character", y="character"), 
                            plugin="Rcpp", body=cCode)
cCharCompare("a", "b")

--

The comparison using == works perfectly fine if one of the two elements is a constant. The following code compiles and gives expected results:

cCode <- '
    Rcpp::CharacterVector cx(x);
    Rcpp::LogicalVector r(1);
    r[0] = (cx[0] == "a");
    return(r);
    '

cCharCompareA <- cxxfunction(signature(x="character"), plugin="Rcpp", body=cCode)

cCharCompareA("a")
[1] TRUE

cCharCompareA("b")
[1] FALSE
Andrie
  • 176,377
  • 47
  • 447
  • 496

3 Answers3

17

The equality operator has been introduced in Rcpp 0.10.4. The implementation looks like this in the string_proxy class:

bool operator==( const string_proxy& other){
    return strcmp( begin(), other.begin() ) == 0 ;
}

So now we can write:

#include <Rcpp.h>
using namespace Rcpp ;

// [[Rcpp::export]]
LogicalVector test( CharacterVector x, CharacterVector y){
    Rcpp::LogicalVector r(x.size());
    for( int i=0; i<x.size(); i++){
        r[i] = (x[i] == y[i]);
    }
    return(r);
}

And something similar is used on our unit tests:

> test(letters, letters)
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Romain Francois
  • 17,432
  • 3
  • 51
  • 77
16

Try this:

  // r[0] = (cx[0] == cy[0]);
  // r[0] = ((char*)cx[0] == (char*)cy[0]); <- this is wrong
  r[0] = (*(char*)cx[0] == *(char*)cy[0]); // this is correct.

It is not easy to explain, but

  1. CharacterVector is not char[].
  2. operator [] returns StringProxy.
  3. StringProxy is not a type of char.
  4. StringProxy has a member operator function char* that convert StringProxy to char*.

So, maybe (char*)cx[0] is a pointer. Now I forget many things about C++ syntax...

The reason hy the compile fails is the failure of type inference in operator overload == for StringProxy.

kohske
  • 65,572
  • 8
  • 165
  • 155
  • +1 Thank you, this works. And thank you for the explanation. (I have tried various forms of coercion to `char`, but the elusive answer was to coerce using `char*`. I suspect there is a bit of `Rcpp` sugar along the lines of `as(cx[0])` - hopefully somebody can comment and give the correct `Rcpp` sugar syntax. – Andrie Oct 24 '11 at 12:30
  • @Andrie The first version is incorrect. Current version is correct. – kohske Oct 24 '11 at 12:32
14

Very nice (technical) answer by @kohske, but here is something more C++-ish: just compare strings!

library(inline)      ## implies library(Rcpp) when we use the plugin

cCode <- '
    std::string cx = Rcpp::as<std::string>(x);
    std::string cy = Rcpp::as<std::string>(y);
    bool res = (cx == cy);
    return(Rcpp::wrap(res));
    '

cCharCompare <- cxxfunction(signature(x="character", y="character"),
                            plugin="Rcpp", body=cCode)
cCharCompare("a", "b")

If you really want to compare just the first character of the strings, then you can go from x to x.c_str() and either index its initial element, or just dereference the pointer to the first char.

A more R-ish answer could maybe sweep over actual vectors of strings...

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • Thank you for this - it's most helpful. My real question is of course sweeping over vectors, but in trying to get to the minimal reproducible example, I realised that my problem is with comparing strings, i.e. comparing individual elements of a CharacterVector. With this bit solved, it should be straight-forward to step up to using the vector operations defined in `Rcpp::Sugar`. – Andrie Oct 24 '11 at 13:06
  • Before going fancy with *Rcpp sugar* (no :: here as there is no class Sugar), I would try old-school with a loop first. – Dirk Eddelbuettel Oct 24 '11 at 13:13