Mark Bower
Mark Bower

Reputation: 589

Returning a user-defined structure from Rcpp in an R package

Rcpp is powerful and has worked great in most all cases, but I cannot figure out how to wrap a C-function that returns a user-defined structure into an R package.

DESCRIPTION

Package: myPackage
Type: Package
Title: What the Package Does (Title Case)
Version: 0.1.0
Depends: R (>= 4.0.0)
License: GPL-3
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.1.0
Imports:
    Rcpp
Suggests: 
    knitr,
    rmarkdown,
    testthat
VignetteBuilder: knitr
LinkingTo: 
    Rcpp

NAMESPACE

# Generated by roxygen2: do not edit by hand

importFrom(Rcpp,evalCpp)
export(read_header)
useDynLib(myPackage, .registration = TRUE)

Header File ("inst/include/myPackage_types.h")

#include <Rcpp.h>

namespace Rcpp {

  typedef struct {
    int my_data;
  } MY_HEADER_INFO;

  template <> SEXP wrap(const MY_HEADER_INFO& x) {
    std::vector<std::string> names;
    std::vector<SEXP> elements(1);
    // do something with the elements and names
    names.push_back("my_data");
    elements[0] = Rcpp::wrap( x.my_data );
    return 0;
  };

}

C-code

/*
        read_header.c
*/

#include <stdio.h>
#include <stdlib.h>

#include <Rcpp.h>
#include "inst/include/myPackage_types.h"

namespace Rcpp {

// [[Rcpp::export]]
  Rcpp::MY_HEADER_INFO read_header() {
    Rcpp::MY_HEADER_INFO *header;
    
    header = (Rcpp::MY_HEADER_INFO*)malloc(sizeof(Rcpp::MY_HEADER_INFO));
    memset(header, 0, sizeof(Rcpp::MY_HEADER_INFO));
    
    return *header;
  }

}

When I source (i.e., compile) the C-code, I get the following:

Error in dyn.load("/private/var/folders/gl/jvj9b0xn34lgq6_h9370p8q80000gn/T/RtmpYRaBGW/sourceCpp-x86_64-apple-darwin17.0-1.0.4.6/sourcecpp_25a13fe3d7da/sourceCpp_49.so") : 
  unable to load shared object ...

When I try to build the package (CMD + Shift + B), I get:

clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG  -I'/Library/Frameworks/R.framework/Versions/4.0/Resources/library/Rcpp/include' -I/usr/local/include   -fPIC  -Wall -g -O2  -UNDEBUG -Wall -pedantic -g -O0 -fdiagnostics-color=always -c RcppExports.cpp -o RcppExports.o
   RcppExports.cpp:9:7: error: no type named 'MY_HEADER_INFO' in namespace 'Rcpp'
   Rcpp::MY_HEADER_INFO read_header();
   ~~~~~~^

This seems so simple, but I can't find a similar vignetter or example and none of the variations I've tried seem to work. What is the cause of the dynamical loading error for SourceCpp? Why can't the compiler find the code in the header file?

Thanks!

Upvotes: 0

Views: 222

Answers (1)

Mark Bower
Mark Bower

Reputation: 589

I got this to work and have posted my solution to GitHub: [email protected]:markrbower/myPackage.git

The key parts are: inst/include/myPackage_types.h

#include <RcppCommon.h>

namespace Rcpp {
  typedef struct {
    int my_data;
  } MY_HEADER_INFO;

  template <> SEXP wrap(const MY_HEADER_INFO& x);
  
  template<> MY_HEADER_INFO* as(SEXP x);
} 

read_header.cpp

/*
        read_header.c
*/

#include <stdio.h>
#include <stdlib.h>

#include "../inst/include/myPackage_types.h"

#include <RcppCommon.h>


namespace Rcpp {

  template <> SEXP wrap(const MY_HEADER_INFO& x);

}

#include <Rcpp.h>

namespace Rcpp {
  template <> SEXP wrap(const MY_HEADER_INFO& x) {
    std::vector<std::string> names;
    std::vector<SEXP> elements(1);
    // do something with the elements and names
    names.push_back("my_data");
    elements[0] = wrap( x.my_data );
    
    Rcpp::List result(elements.size());
    for (size_t i = 0; i < elements.size(); ++i) {
      result[i] = elements[i];
    }
    result.attr("names") = Rcpp::wrap(names);
    // result can be return to R as a list   
    return( result );
  };
}

//' @importFrom Rcpp evalCpp
//' @useDynLib myPackage
//' @export
// [[Rcpp::export]]
  Rcpp::MY_HEADER_INFO read_header() {
    Rcpp::MY_HEADER_INFO *header = NULL;
    
    printf( "%ld\n", sizeof(Rcpp::MY_HEADER_INFO) );
    
    header = (Rcpp::MY_HEADER_INFO*)malloc(sizeof(Rcpp::MY_HEADER_INFO));
    memset(header, 0, sizeof(Rcpp::MY_HEADER_INFO));
    
    header->my_data = 10;
    
    return *header;
}

There were two problems. First, I had template definitions under the wrong Rcpp header (put the initial calls after RcppCommon.h and the detailed calls after Rcpp.h). The tutorials and examples warned me not to do that, but I did it, anyway. Second, I found that if you "source" the code and then "load" the library, the sourced code will obscure the library code and you will get a "null pointer" error. Running "devtools::check()" showed me that along with noting that the fix is to "rm" the sourced function.

Let me also add that there are two Roxygen comments that I needed to add to my .cpp file to get the appropriate commands to appear in my NAMESPACE file: //' @importFrom Rcpp evalCpp //' @useDynLib myPackage

I found an old post where Dirk suggested using the Rcpp.package.skeleton function to build a "baby" project and then slowly add things until you can do what you want. That is so much better than the approach I have been using: Start with a complex C program and try to shoehorn my code into Rcpp.

Upvotes: 1

Related Questions