1

I would like to provide a C++ interface for a function within a package which return std::pair using Rcpp::interface. However, the compiler throws a shitload of errors, starting with:

.../Rcpp/include/Rcpp/internal/Exporter.h:31:31: error: no matching
function for call to ‘std::pair<int, int>::pair(SEXPREC*&)’
   Exporter( SEXP x ) : t(x){}

Here is a simple example:

#include <Rcpp.h>
#include <utility>

// [[Rcpp::interfaces(cpp)]]

// [[Rcpp::export]]
std::pair<int, int> bla()
{
  return std::make_pair(1,1);
}

The generated code for this example function looks like:

inline std::pair<int, int> bla() {
    typedef SEXP(*Ptr_bla)();
    static Ptr_bla p_bla = NULL;
    if (p_bla == NULL) {
        validateSignature("std::pair<int, int>(*bla)()");
        p_bla = (Ptr_bla)R_GetCCallable("testinclude", "testinclude_bla");
    }
    RObject rcpp_result_gen;
    {
        RNGScope RCPP_rngScope_gen;
        rcpp_result_gen = p_bla();
    }
    if (rcpp_result_gen.inherits("interrupted-error"))
        throw Rcpp::internal::InterruptedException();
    if (rcpp_result_gen.inherits("try-error"))
        throw Rcpp::exception(as<std::string>(rcpp_result_gen).c_str());
    return Rcpp::as<std::pair<int, int> >(rcpp_result_gen);
}

Is this a bug or what is going wrong here?

nrussell
  • 18,382
  • 4
  • 47
  • 60
NoBackingDown
  • 2,144
  • 1
  • 19
  • 25
  • 2
    It's a bug in your understanding. You can only return back to R what can be mapped to a `SEXP`. And `std::pair<>` cannot. You could turn it into integer vectors, say. – Dirk Eddelbuettel Feb 15 '17 at 23:23
  • @DirkEddelbuettel But I don't want to return it back to R at all, that's why I use only `Rcpp::interfaces(cpp)`. As per my understanding, this creates a pure C++ Interface. I simply want to use some C++ functions in another package. If it is not feasible with `Rcpp::interfaces`, how do you do it then? – NoBackingDown Feb 16 '17 at 07:01
  • The use of `[[Rcpp::export]]` trigger the `Rcpp::as<>` which fails as there is no converter for `std::pair`. It does not work this way. The interface feature may work _on top_ of an interface to R, not instead of. Check the 'Rcpp Attributes' vignette. – Dirk Eddelbuettel Feb 16 '17 at 12:30
  • @DirkEddelbuettel So is there a way of simply exposing all the .hpp and .cpp files of one package such that another package can include them, compiling everything itself? – NoBackingDown Feb 16 '17 at 12:34
  • You still think you have a misunderstanding of how R packages talk to each other -- see "Writing R Extensions" about that --- and too much optimism about how we should be able to change that. – Dirk Eddelbuettel Feb 16 '17 at 12:45
  • @DirkEddelbuettel So the only possible way of calling C++ functions of another package from C++ is via the R_RegisterCCallable/R_GetCCallable mechanism as specified in 5.4.2 of "Writing R Extensions"? – NoBackingDown Feb 16 '17 at 13:08
  • That is the documented and supported and portable and official way. Which, by the way, Rcpp has nothing to do with, so you started in the wrong corner. I make no claims as to exlusivity and presence/absence of other ways. But I continue to point out to you that you have basic premises wrong with your question here. Good luck, I think I stop here now. – Dirk Eddelbuettel Feb 16 '17 at 13:10

1 Answers1

7

However, the compiler throws a shitload of errors, starting with:

.../Rcpp/include/Rcpp/internal/Exporter.h:31:31: error: no matching
function for call to ‘std::pair<int, int>::pair(SEXPREC*&)’
   Exporter( SEXP x ) : t(x){}

As pointed out by Dirk, this error (and generally any error citing Exporter.h or wrap.h) was triggered by the use of the // [[Rcpp::export]] attribute, which (attempts) to generate the appropriate boilerplate code needed to turn the std::pair<int, int> into something that R knows how to handle (i.e., some type of SEXP).

Based on your comment

But I don't want to return it back to R at all [...]

you are in a good situation because this means you won't have to go through the trouble of writing a converter function that handles std::pair<int, int> -- simply remove the // [[Rcpp::export]] declaration, and that will take care of the above error message.


On to the meat of the problem,

I simply want to use some C++ functions in another package

I can propose two approaches for you, and incidentally, neither of them use the // [[Rcpp::interfaces]] attribute.


The Easy Way

I assume the example you provided is a simplification of your actual use case, but if at all possible, do everything in your power to provide a header-only interface. While there are potential drawbacks to this approach (e.g. discussed in this question), it will greatly simplify what you are trying to do, and IMO, this far outweighs the cost of an extra couple of minutes of compilation time. If you were planning on providing an interface of strictly template classes and / or functions then life is good, because this would be your only option anyways.

To demonstrate, consider the following directory structure for the interface package:

# nathan@nathan-deb:/tmp$ tree hinterface/
# hinterface/
# ├── DESCRIPTION
# ├── inst
# │   └── include
# │       ├── hinterface
# │       │   └── hinterface.hpp
# │       └── hinterface.h
# ├── NAMESPACE
# ├── R
# └── src
#     ├── hinterface.cpp
#     ├── Makevars
#     └── Makevars.win

You begin by creating the directories inst/ and inst/include/, as this will cause R to copy the header file hinterface.h into the hinterface library directory when the package is installed on a user's machine. Additionally, I've created inst/include/hinterface/, and hinterface.hpp, which contains the implementation:

#ifndef hinterface__hinterface__hpp
#define hinterface__hinterface__hpp

#include <Rcpp.h>
#include <utility>

namespace hinterface {

inline std::pair<int, int> bla()
{ return std::make_pair(1, 1); }

} // hinterface

#endif // hinterface__hinterface__hpp

This is not strictly necessary, but it is a reasonable convention to follow, especially if you have many header files. Moving back up one level, the hinterface.h file -- which clients will actually include in their source code -- contains this:

#ifndef hinterface__hinterface__h
#define hinterface__hinterface__h

#include "hinterface/hinterface.hpp"
// possibly other
// header files
// to include

#endif // hinterface__hinterface__h

In the src/ directory, create a Makevars and Makevars.win, each containing

PKG_CPPFLAGS = -I../inst/include

and any other necessary compiler options you may need to set. Finally, I've added a dummy source file just to enable the package to build, but if you were actually exporting one or more C++ functions this would not be necessary:

#include "hinterface.h"

void noop() { return; }

In the package hclient, which will be calling bla from the hinterface package, things are even simpler:

# nathan@nathan-deb:/tmp$ tree hclient/
# hclient/
# ├── DESCRIPTION
# ├── NAMESPACE
# ├── R
# └── src
#     ├── hclient.cpp

All a user needs to do (assuming the packages was generated from R via Rcpp::Rcpp.package.skeleton) add hinterface to the LinkingTo field in the DESCRIPTION file,

LinkingTo: Rcpp,
    hinterface

add a call to the // [[Rcpp::depends(hinterface)]] attribute in their source file, and include hinterface.h:

// hclient.cpp
// [[Rcpp::depends(hinterface)]]
#include <Rcpp.h>
#include <hinterface.h>

// [[Rcpp::export]]
void call_bla()
{
    std::pair<int, int> x = hinterface::bla();
    std::printf(
        "x.first = %d\nx.second = %d\n",
        x.first, x.second
    );
}

Building this package, we can see that it works as expected by calling it from R:

hclient::call_bla()
# x.first = 1
# x.second = 1

The Hard Way

In this approach, since you will truly only be providing an interface in your header files (and thus code in client packages will need to link to the implementation), you will need to jump through hoops to appease the linker, which is never a fun time. Futhermore, it places more of a burden on the client package than before, although you can mitigate this to some degree as shown later.

Without further ado, interface is laid out like this:

# nathan@nathan-deb:/tmp$ tree interface/
# interface/
# ├── DESCRIPTION
# ├── inst
# │   └── include
# │       └── interface.h
# ├── NAMESPACE
# ├── R
# │   └── libpath.R
# └── src
#     ├── bla.cpp
#     ├── Makevars
#     └── Makevars.win

Since we are no longer implementing bla in a *.hpp or *.h file, the interface header, interface.h, will only contain a function prototype:

#ifndef interface__interface__h
#define interface__interface__h

#include <Rcpp.h>
#include <utility>

namespace interface {

std::pair<int, int> bla();

} // interface

#endif // interface__interface__h

As before, Makevars and Makevars.win simply contain PKG_CPPFLAGS = -I../inst/include (and other flags you may need to set). bla.cpp is pretty straight forward, and just contains the appropriate implementation:

#include "interface.h"

namespace interface {

std::pair<int, int> bla()
{ return std::make_pair(1, 1); }

} // interface

As alluded to, client packages will need to link their code to interface in order to actually use bla() -- and by linking to I don't mean just adding interface to the LinkingTo field in the DESCRIPTION file, which ironically has nothing to do with the linking phase in compilation. Failure to do this -- e.g. only including the header interface.h -- will cause R CMD INSTALL to halt, as it will fail to find the appropriate symbols when it attempts to load the client package. This can be a very frustrating error for users to deal with. Fortunately, you can help make things easier by providing something like the following function, which generates the location of the interface shared library:

# libpath.R
.libpath <- function() {
    cat(sprintf(
        "%s/interface/libs/interface%s",
        installed.packages()["interface","LibPath"][1],
        .Platform$dynlib.ext
    ))
}

I named this beginning with a . so that it would not be exported (by default) and thus not require being documented. If you intended for people to uses your C++ interface in their own packages, you should export the function and document it appropriately so that they understand how to use it. The path it returns will depending entirely on the specific R installation it is called from (necessarily), but on my Linux machine it looks like this:

interface:::.libpath()
# /home/nathan/R/x86_64-pc-linux-gnu-library/3.4/interface/libs/interface.so

Moving on to the aptly named client package,

# nathan@nathan-deb:/tmp$ tree client/
# client/
# ├── DESCRIPTION
# ├── NAMESPACE
# ├── R
# └── src
#     ├── client.cpp
#     ├── Makevars
#     ├── Makevars.win

The DESCRIPTION file again needs

LinkingTo: Rcpp,
        interface

so that the header file is located correctly. The source file, client.cpp, looks like this:

// [[Rcpp::depends(interface)]]
#include <Rcpp.h>
#include <interface.h>

// [[Rcpp::export]]
void call_bla()
{
    std::pair<int, int> x = interface::bla();
    std::printf(
        "x.first = %d\nx.second = %d\n",
        x.first, x.second
    );
}

This is no different than the source file in the hclient package. Where things get interesting is in Makevars and Makevars.win, which now contain

PKG_LIBS += `${R_HOME}/bin/Rscript -e "cat(interface:::.libpath())"`

Here we are using the helper function defined in the interface package to make sure the appropriate symbols are available to the linker. Building this and testing it out from R,

client::call_bla()
# x.first = 1
# x.second = 1

Community
  • 1
  • 1
nrussell
  • 18,382
  • 4
  • 47
  • 60
  • Thank you so much, this was precisely what is was looking for! I decided to first try the "Easy way", to which I have a couple of follow-up questions. i) Calling the function ` hinterface::bla()` from the "client" works, but how do I call it from the `hinterface` package? I tried to `#include "hinterfaces.h` in some other .cpp file in the src directory, but ran into a multiple definition problem then. – NoBackingDown Feb 17 '17 at 23:12
  • (ii) I would like to export `bla()` to R in the `hinterface` package with `// [[Rcpp::export]]`, but this works only for files in the src directory. Is there another option or do I need to write a wrapper and put it into src? This is where I originally got to problem (i). (iii) In my real application, I'm using the boost library. As the "Easy way" is header only, I guess that there is no other way than also do `// [[Rcpp::depends(BH)]]` and `LinkingTo: BH` at the side of the client package? I suppose the "hard way" would solve this "problem". – NoBackingDown Feb 17 '17 at 23:15
  • Re: **(i)** and **(ii)**, exporting a function to R named `bla` can be done as in the second function [here](https://gist.github.com/nathan-russell/8c7164504459318e01804261469d4f8e#file-hinterface-cpp); just give it a dummy name in the .cpp file (e.g. `bla_`), and tag it with `// [[Rcpp::export("bla")]]` instead of `// [[Rcpp::export]]`, which by default names the R function the same as the C++ function. For **(iii)**, it would depend on the specific library -- some Boost libraries are header only and some aren't. Which one are you using? – nrussell Feb 17 '17 at 23:42
  • Awesome, I had to lear the hard way that the `inline` keyword is mandatory for functions defined in header files, but now everything seems to work! To your question, the boost library is `#include `. – NoBackingDown Feb 18 '17 at 12:43
  • Is it okay to use `inline` for large functions? – F. Privé Jul 29 '17 at 06:58
  • It's just a hint. The compiler is free to ignore it. – nrussell Jul 29 '17 at 09:11