2

Question

In an R package, how do I return an Rcpp::List to a C function?

Example

I have a package hosted on my github page to illustrate the requirement.

It contains an R function, which calls a C function, which I would like to call the C++ function to retrieve the list.

R

#' @useDynLib crcpp c_ask_for_list
r_ask_for_list <- function() {
    .Call(c_ask_for_list)
}

C

#include <Rinternals.h>

SEXP c_ask_for_list (){
    SEXP l = PROTECT(allocVector(VECSXP, 1));
    //l = rcpp_create_list();          // Call the C++ function to create the list
    UNPROTECT(1);
    return(l);
}

C++

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
extern "C" SEXP rcpp_create_list() {
    Rcpp::List l(1);
    l[0] = "foo";
    return l;
}

If I uncomment the l = rcpp_create_list() line in the C function the program crashes.

SymbolixAU
  • 25,502
  • 4
  • 67
  • 139
  • 2
    You need headers if you want to share functions between several files, see https://stackoverflow.com/questions/6995572/using-multiple-cpp-files-in-c-program – Alexis Jul 05 '18 at 06:33
  • What is not working? Which error message do you get at what point in the process? – Ralf Stubner Jul 05 '18 at 10:30
  • @RalfStubner - I've added an extra note at the end. If i uncomment the `l = rcpp_create_list()` function in the C program, the program crashes – SymbolixAU Jul 05 '18 at 11:12
  • I think you painted yourself into a particular corner thanks to roxygen2, NAMESPACE autowriting, interface autocreation, function registration and that lack of headers. You are close; I just glanced at your repo on the train ride to work. I can't quite put my finger on the error, but I think @Alexis is closes. See the Rcpp Attributes vignette. We can definitely do this. Just some details to work out. – Dirk Eddelbuettel Jul 05 '18 at 13:30
  • Also: _how do I return an Rcpp::List to a C function?_ Don't overcomplicate. At the C level *everything* is a `SEXP` and every Rcpp function follows that. We _always_ use C interfaces. – Dirk Eddelbuettel Jul 05 '18 at 13:31
  • I cannot reproduce the error: The code compiles and works. However, my compiler gives two warning messages: `warning: implicit declaration of function ‘rcpp_create_list’` and `warning: initialization makes pointer from integer without a cast`. When I add `SEXP rcpp_create_list();` to the C file, the warnings go away. – Ralf Stubner Jul 05 '18 at 16:06
  • @RalfStubner - I can't believe it was that simple! adding `SEXP rcpp_create_list()` to the `c` file stops my Rstudio crashing, removes the warnings and returns me the list... – SymbolixAU Jul 05 '18 at 22:05
  • I've updated the github repo with a working version, and have included headers (even though it worked without them) – SymbolixAU Jul 05 '18 at 22:13

1 Answers1

2

Thanks to all the pointers I have the program working as intended. The full structure now is

R

#' @useDynLib crcpp c_ask_for_list
#' @export
r_ask_for_list <- function() {
    .Call("c_ask_for_list", packages = "crcpp")
}

C

file: c_ask_for_list.h

#ifndef CRCPP_H
#define CRCPP_H

#include <Rinternals.h>

#ifdef __cplusplus
extern "C" {
#endif

SEXP rcpp_create_list();

#ifdef __cplusplus
}
#endif

#endif /* CRCPP_H */

file: c_ask_for_list.c

#include "c_ask_for_list.h"

SEXP c_ask_for_list () {
    SEXP l = _crcpp_rcpp_create_list();          // Call the C++ function to create the list
    return(l);
}

C++

file: rcpp_create_list.cpp

#include <Rcpp.h>
using namespace Rcpp;

//' @importFrom Rcpp evalCpp
//' @export
// [[Rcpp::export]]
SEXP rcpp_create_list() {
    Rcpp::List l(2);
    l[0] = "foo";
    l[1] = "bar";
    return l;
}
SymbolixAU
  • 25,502
  • 4
  • 67
  • 139
  • The header structure is unusual. Here you have two headers that declare the same function. Normally there would be one header file `rcpp_create_list.hpp` (or `.h`). In there the `extern "C"` of the function declaration would be protected by `#ifdef __cplusplus`. This header file would then be included in in the C file (and optionally in the C++ file). – Ralf Stubner Jul 06 '18 at 07:58
  • Also, in `c_ask_for_list` you can simply write `SEXP l = rcpp_create_list();`, without needing `PROTECT` or `UNPROTECT`. – Alexis Jul 06 '18 at 19:53
  • @Alexis - are you sure that's correct. If I remove the `... PROTECT .. ` line the program crashes at runtime. – SymbolixAU Jul 07 '18 at 06:10
  • @RalfStubner - thanks; I've edited the answer in line with your comments. – SymbolixAU Jul 07 '18 at 06:12