Frank Harrell
Frank Harrell

Reputation: 2230

Rcpp C++ binary object not found when adding a new function to an old R package

I am running the latest R and Rcpp under MacOS Sonoma. I am attempting to add a new function call C++ code to a very old package (Hmisc), setting things up using Rcpp::compileAttributes(). The new R function's .Call() to the C++ binary object results in object not found. Here's what I have in place:

// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include <Rcpp.h>

using namespace Rcpp;

#ifdef RCPP_USE_GLOBAL_ROSTREAM
Rcpp::Rostream<true>&  Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

// hlqest
double hlqest(NumericVector x);
RcppExport SEXP _Hmisc_hlqest(SEXP xSEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
    rcpp_result_gen = Rcpp::wrap(hlqest(x));
    return rcpp_result_gen;
END_RCPP
}
hlqest <- function(x) {
    .Call(`_Hmisc_hlqest`, x)
}
#' Pseudomedian
#'
#' Uses fast C++ code from the `DescTools` package to compute the pseudomedian of a numeric vector.  The pseudomedian is the median of all possible midpoints of two different observations.  The C++ code is originally from Monahan and Moser.  The pseudomedian is also called the Hodges-Lehmann one-sample estimator.
#' @title pMedian
#' @param x a numeric vector
#' @param na.rm set to `TRUE` to exclude `NA`s before computing the pseudomedian
#'
#' @return a scalar numeric value
#' @export
#' @md
#' @seealso <https://dl.acm.org/doi/10.1145/1271.319414>, <https://www4.stat.ncsu.edu/~monahan/jul10>
#' @examples
#' x <- 1:5
#' pMedian(x)
#' # Compare with brute force calculation and with wilcox.test
#' median(outer(x, x, '+')) / 2
#' wilcox.test(x, conf.int=TRUE)

pMedian <- function(x, na.rm = FALSE) {
  if(na.rm) x <- x[! is.na(x)]
  n <- length(x)
  if(n == 0) return(NA_real_)
  if(n == 1) return(as.double(x))
  .Call(`_Hmisc_hlqest`, x)
}

After building the binary Hmisc package and loading the package I get the following error upon running pMedian(1:5): Error in pMedian(1:5) : object '_Hmisc_hlqest' not found. Any help is appreciated.

Upvotes: 0

Views: 56

Answers (1)

Ada Lovelace
Ada Lovelace

Reputation: 1265

The idea is to put // [[Rcpp::export]] above your function hlquest.

What RcppExports.cpp and RcppExports.R then do is to ensure you can an R-callable hlquest(x) -- as that is easier / simpler than the expression you show above, namely

.Call(`_Hmisc_hlqest`, x)

One or two more other things to pay attention to is that the NAMESPACE has a line

useDynLib("Hmisc", .registration=TRUE)

as the .Call above does not pass a string and does not reference the package. (And you show you have that, which is something I missed on my first read.)

I have also found that adding to an existing package is hardest in the sense that for a 'new' package without an src/init.c the corresponding entries are automatically created in RcppExports.cpp but if I remember correct the compileAttributes() call is careful and will not modify an existing init.c. So it is finicky -- but one you dot all i-s and cross all t-s it should work.

And as you write

src/init.c: several references to the old Fortran routines, nothing about the new C++ routine

it might be best to check with a throw-away one-function package you can create what would be needed and to then add that manually to your existing src/init.c.

Upvotes: 1

Related Questions