Allow C++ constants to be a default function parameter using Rcpp Attributes

后端 未结 1 1978
栀梦
栀梦 2021-01-14 03:44

I created a cumsum function in an R package with rcpp which will cumulatively sum a vector until it hits the user defined ceiling or floor. However, if one wants the cumsum

1条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-14 04:25

    In short, yes its possible but it requires finesse that involves creating an intermediary function or embedding sorting logic within the main function.


    In long, Rcpp attributes only supports a limit feature set of values. These values are listed in the Rcpp FAQ 3.12 entry

    • String literals delimited by quotes (e.g. "foo")
    • Integer and Decimal numeric values (e.g. 10 or 4.5)
    • Pre-defined constants including:
      • Booleans: true and false
      • Null Values: R_NilValue, NA_STRING, NA_INTEGER, NA_REAL, and NA_LOGICAL.
    • Selected vector types can be instantiated using the empty form of the ::create static member function.
      • CharacterVector, IntegerVector, and NumericVector
    • Matrix types instantiated using the rows, cols constructor Rcpp::Matrix n(rows,cols)
      • CharacterMatrix, IntegerMatrix, and NumericMatrix)

    If you were to specify numerical values for LLONG_MAX and LLONG_MIN this would meet the criteria to directly use Rcpp attributes on the function. However, these values are implementation specific. Thus, it would not be ideal to hardcode them. Thus, we have to seek an outside solution: the Rcpp::Nullable class to enable the default NULL value. The reason why we have to wrap the parameter type with Rcpp::Nullable is that NULL is a very special and can cause heartache if not careful.

    The NULL value, unlike others on the real number line, will not be used to bound your values in this case. As a result, it is the perfect candidate to use on the function call. There are two choices you then have to make: use Rcpp::Nullable as the parameters on the main function or create a "logic" helper function that has the correct parameters and can be used elsewhere within your application without worry. I've opted for the later below.

    #include 
    #include 
    #include 
    #include  //for LLONG_MIN and LLONG_MAX
    using namespace Rcpp;
    
    NumericVector cumsum_bounded_logic(NumericVector x,
                                       long long int upper = LLONG_MAX,
                                       long long int lower = LLONG_MIN) {
    
        NumericVector res(x.size());
        double acc = 0;
        for (int i=0; i < x.size(); ++i) {
            acc += x[i];
            if (acc < lower)  acc = lower;
            else if (acc > upper)  acc = upper;
            res[i] = acc;
        }
    
        return res;
    }
    
    // [[Rcpp::export]]
    NumericVector cumsum_bounded(NumericVector x,
                                 Rcpp::Nullable upper = R_NilValue, 
                                 Rcpp::Nullable lower = R_NilValue) {
    
        if(upper.isNotNull() && lower.isNotNull()){
            return cumsum_bounded_logic(x, Rcpp::as< long long int >(upper), Rcpp::as< long long int >(lower));
        } else if(upper.isNull() && lower.isNotNull()){
            return cumsum_bounded_logic(x, LLONG_MAX, Rcpp::as< long long int >(lower));
        } else if(upper.isNotNull() && lower.isNull()) {
            return cumsum_bounded_logic(x, Rcpp::as< long long int >(upper), LLONG_MIN);
        } else {
            return cumsum_bounded_logic(x, LLONG_MAX, LLONG_MIN);
        }
    
        // Required to quiet compiler
        return x;
    }
    

    Test Output

    cumsum_bounded(a, 5)
    ## [1] 1 2 3 4 5 5 5
    cumsum_bounded(a, 5, 2)
    ## [1] 2 3 4 5 5 5 5
    

    0 讨论(0)
提交回复
热议问题