2

New to C++, I would like to make functions compiled in a DLL available in R.

So far, I managed to do that for a basic function taking integer as input and returning the same, by following these steps in VisualStudio, then using dyn.load from R to load the created dll.

However, my C++ functions will need to handle R data.frame objects, and I am not sure how to make that possible. I saw from the Rcpp gallery that Rcpp might include some kind of translations between R and c++ data types including data.frame objects, but I don't know if I can generate a dll using Rcpp that I can then include in R using dyn.load.

From this answer by Dirk Eddelbuettel, it seems possible to generate a "dynamic library" using Rcpp, however, I could not find any dll when I tried generating a package with a source .cpp file using rcpp.package.skeleton(). The function I'd like to have a dll for is from the Rcpp gallery

#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
DataFrame modifyDataFrame(DataFrame df) {
  // access the columns
  IntegerVector a = df["a"];
  CharacterVector b = df["b"];

  // make some changes
  a[2] = 42;
  b[1] = "foo";       
  // return a new data frame
  return DataFrame::create(_["a"]= a, _["b"]= b);
}

I tried to just paste that code into VisualStudio to try and generate that DLL, however I have the error "cannot find Rcpp.h" which I quite expected.

I then followed these steps in RStudio:

  1. Create new project / Package
  2. Include this cpp source file as a source file for this package
  3. include Rcpp and enter Rcpp.package.skeleton("mypackage") so far, no DLL in the package folders
  4. Tried to build the package in RStudio by going to Build/Install and Restart, but then I get an error message "Building R Packages needs installation of additional build tools, do you want to install them?" However I already have RbuildTools 3.4 installed, and when I click "YES" in RStudio nothing happens.

PS: Happy to hear about other methods but here the DLL format should be used if possible. Any piece of info is greatly appreciated since I have basically no idea of how Rcpp or C++ work

gaut
  • 5,771
  • 1
  • 14
  • 45
  • First off: Where in our copious documentation do we ask you to use _Visual Studio_ ? Correct answer, nowhere. In fact the Rcpp FAQ explicitly says not to. Second: `Rcpp.package.skeleton()` creates _sources_ whereas a DLL is the result of a _compilation and link_ step. Did you compile and link? – Dirk Eddelbuettel Nov 03 '18 at 22:08
  • The reason I used VisualStudio is that I haven't found yet in your copious documentation how to make functions available in a standalone DLL - if I missed that point, please point me to the right source, thanks – gaut Nov 03 '18 at 22:17
  • 1
    1. Create a package containing to-be-compiled source code, eg via `Rcpp.package.skeleton()`. 2. Build the package, eg via `R CMD build` and `R CMD INSTALL` (or by hitting buttons in RStudio). If between steps one and two you copy a valid file in the `src/` directory, it will be part of of the package shared library. _That is how R works_ and has nothing to with Rcpp per se. Also note that only of of the OSs that R is used on defines the term "DLL". So don't obsess over it -- Linux and macOS will call it a shared library file with extensions `.so` and `.dylib`, respectively. – Dirk Eddelbuettel Nov 03 '18 at 22:28
  • And most importantly: Don't make up your own rules (ie: "compile with Visual Studio") and expect things to work like that. The world does not owe you that favour. – Dirk Eddelbuettel Nov 03 '18 at 22:30
  • I am not trying to make up any rules at all, just using what I found on the net. I am here to humbly request help and clarifications, but I'm going to ask you to abstain if you are to use such a tone. I am sure we both have a lot of shit to deal with outside of StackOverflow – gaut Nov 03 '18 at 22:40
  • Instead of repeating the same question over and over, why don't you *read* my comment from seventeen minutes ago which has the answer? You assumption is still wrong so "you're still making up your rules". It doesn't work that way. `Rcpp.package.skeleton()` is *not* the function that compiles and links and hence *not* the one creating a shared library. – Dirk Eddelbuettel Nov 03 '18 at 22:45
  • @DirkEddelbuettel it's not super clear but I think the OP wants to have the result of their work be a DLL that they can use _in other projects_, i.e. not from R but in other compiled code, Windows executables. – hrbrmstr Nov 03 '18 at 23:25

2 Answers2

3

You need to figure out why your setup is hosed. This is meant to be easy and it is easy. Watch:

R> Rcpp::cppFunction('DataFrame modDF(DataFrame df) { IntegerVector a = df["a"]; CharacterVector b = df["b"]; a[2] = 42; b[1] = "foo"; return DataFrame::create(Named("a")=a, Named("b")=b); } ')                  
R> df <- data.frame(a=1:3, b=letters[24:26])                                                                                                                                                                       
R> df                                                                                                                                                                                                              
  a b                                                                                                                                                                                                              
1 1 x                                                                                                                                                                                                              
2 2 y                                                                                                                                                                                                              
3 3 z                                                                                                                                                                                                              
R> modDF(df)                                                                                                                                                                                                       
   a   b                                                                                                                                                                                                           
1  1   x                                                                                                                                                                                                           
2  2 foo                                                                                                                                                                                                           
3 42   z                                                                                                                                                                                                           
R>      

Now, I obviously don't recommend writing this way in a long one-liner. You are already on the right track setting up a package. But you need to sort out what is holding up your tools.

And as a PS the one-liner above with better indentation:

R> Rcpp::cppFunction('DataFrame modDF(DataFrame df) { \
      IntegerVector a = df["a"]; \
      CharacterVector b = df["b"]; \
      a[2] = 42;  \
      b[1] = "foo"; \ 
      return DataFrame::create(Named("a")=a, Named("b")=b); \
    } ')                  
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
2

The following seems to work:

  1. from R, ran Rcpp.package.skeleton("dfcpp4", cpp_files="modifyDataFrame.cpp"). The second argument is required in order for the modifyDataFrame function to be available from the dll using dyn.load.
  2. From the command line ran R CMD build dfcpp4
  3. ran R CMD check dfcpp4 --no-manual from the command line.
  4. The dll file in now present in the src-x64 folder

I am now able to call this function using

dyn.load("dfcpp4/src-x64/dfcpp4.dll")
df <- data.frame(a = c(1, 2, 3),
                 b = c("x", "y", "z"))
.Call("_dfcpp4_modifyDataFrame", df)
   a   b
1  1   x
2  2 foo
3 42   z

What I don't get is why in this case .Call should be used instead of .C...

gaut
  • 5,771
  • 1
  • 14
  • 45
  • For Pete's sake please read a modicum of Rcpp docs -- for example the [updated Rcpp Introduction vignette](https://cloud.r-project.org/web/packages/Rcpp/vignettes/Rcpp-introduction.pdf). You do *not need* to call `dyn.load()` yourself. You do *not need* to call `.Call()` yourself. Rcpp Attributes and its code generators that all for you. Look eg at package [RcppExamples](https://github.com/eddelbuettel/rcppexamples). – Dirk Eddelbuettel Nov 04 '18 at 01:35
  • Just call `Rcpp::compileAttributes()` after you copied that C++ file into `src/`. It will updated `src/RcppExports.cpp` and `R/RcppExports.R`. Once you build and install you should now have `modifyDataFrame()` *directly callable from R*. – Dirk Eddelbuettel Nov 04 '18 at 01:38
  • Please read the question, I do **not** want to install a package at this point, I just want to use the dll. – gaut Nov 04 '18 at 01:43
  • You still misunderstand. The dll does not matter. It is a by-product. It is made for you and remains hidden in the package, or the tools (ie `Rcpp::cppFunction()`). Your issue may be your point 4) above: correct your RStudio / Rtools setup and it all becomes a breeze. No point running around angrily til that is fixed -- you are wasting your time, and ours unless we just ignore you. – Dirk Eddelbuettel Nov 04 '18 at 01:46
  • This might not be how you intended Rcpp to be used, but still my question is as valid as any other - plus it taught me few (small) things about a) how Rcpp works and b) what not to talk about on stackoverflow :) If that'll make it easier for you then be sure that I will learn more about your great work and use it in the intended way as long as this way also meets my requirements – gaut Nov 04 '18 at 01:57
  • 1
    If you want to decompose the commands look at the old examples with package `inline` instead, and maybe some of my old slide decks from 2009 to 2012 or so -- still posted on my site. They have all the old-style examples. If you must, you can. *It is still "wrong" to manually call `dyn.load()` because you now introduce an OS dependency we currently do not have.* Your call. – Dirk Eddelbuettel Nov 04 '18 at 01:59
  • And/or: Ignore Rcpp. Read just _Writing R Extensions_ and learn a C extension. Generalize to a C++ extension. Then redo it in one quarter of the code using Rcpp _for either C or C++_. Good learning exercise if you have the patience, and good fodder for a blog post. Now, *here* we focus on simpler answers to help others who may stumble upon them. Cheers. – Dirk Eddelbuettel Nov 04 '18 at 02:02
  • Ok! keep you posted – gaut Nov 04 '18 at 02:12