4

I'm trying to develop an R package, which makes use of Arrayfire, thanks to Rcpp library. I've started writing a sample code (let's name it hello_world.cpp) which looks like this:

#include <arrayfire.h>

// [[Rcpp::export]]
bool test_array_fire(){
    af::randu(1, 4);    
    return true;
}

Then, I tried to compile it using a sourceCpp function

Rcpp::sourceCpp('src/hello_world.cpp')

My first suprise was the fact I had to set some flags manually (sourceCpp seems to ignore Makevars config when compiling a piece of C++ code). I did it with:

Sys.setenv("PKG_CXXFLAGS"="-std=c++11")
Sys.setenv("PKG_CPPFLAGS"="-I/opt/arrayfire/include/")
Sys.setenv("PKG_LIBS"="-L/opt/arrayfire/lib64/ -laf")

However, the code still does not compile properly. Each trial finishes with the following output:

Error in 'dyn.load("/tmp/RtmpHaODIU/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_689c5adb8d/sourceCpp_14.so")':
unable to load shared object '/tmp/RtmpHaODIU/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_689c5adb8d/sourceCpp_14.so':
  libaf.so.3: cannot open shared object file: No such file or directory

Unfortunately, I could not find an solution for my problem (even if some Stack Overflow questions raise issues which are more or less similar at first glance).

How can I fix it?

KrzJoa
  • 71
  • 2
  • 4

2 Answers2

8

The error occurs very late in the process when R tries to load the shared object file. This means that compilation and linking worked fine with the help of the environment variables you set. But in the final step the run-time linker does not know where libaf.so.3 is located. This is a configuration one normally does at the OS level, e.g. on my system

ralf@barra:~$ /sbin/ldconfig -p | grep libaf
        libafopencl.so.3 (libc6,x86-64) => /lib/libafopencl.so.3
        libafopencl.so (libc6,x86-64) => /lib/libafopencl.so
        libafcpu.so.3 (libc6,x86-64) => /lib/libafcpu.so.3
        libafcpu.so (libc6,x86-64) => /lib/libafcpu.so
        libaf.so.3 (libc6,x86-64) => /lib/libaf.so.3
        libaf.so (libc6,x86-64) => /lib/libaf.so

And if I try your example it works without problems with a shared object file that is linked against libaf:

ralf@barra:~$ ldd /tmp/RtmpcjY9dN/sourceCpp-x86_64-pc-linux-gnu-1.0.2/sourcecpp_13d33790279c/sourceCpp_7.so | grep libaf
        libaf.so.3 => /lib/libaf.so.3 (0x00007f21037ed000)

I expect that in your case the first command will provide no result and the second (adjusted) command will lead a "file not found"(?) error.

There are several ways to tell the run-time linker about the location of the library:

  • Edit /etc/ld.so.conf or (better) place a file in /etc/ld.so.conf.d/, c.f. http://arrayfire.org/docs/installing.htm#Linux.
  • Set LD_LIBRARY_PATH.
  • Add -Wl,-rpath,/opt/arrayfire/lib64/ to PKG_LIBS
  • Install ArrayFire into a directory that the linker searches by default. That's what I am doing since I compile from source and use the resulting DEB packages.

As for Rcpp::sourceCpp not respecting a Makevars file: The problem is that the C++ file you write cannot be used directly. Instead Rcpp attributes has to create additional wrapper functions, which is done in a temporary directory. Now one could in principle copy a Makevars file into that directory as well. However, one normally sets such variables with the help of Rcpp::plugins and Rcpp::depends attributes. For example, switching on C++11 is done using // [[Rcpp::plugins(cpp11)]]. For the other variables, you could write your own plugin or us the one provided by my RcppArrayFire.

However, I suggest you start with a package if that is your goal. Rcpp::sourceCpp is great for many things, but interfacing with a system installed library without the help of an R package is not one of them.

Ralf Stubner
  • 26,263
  • 3
  • 40
  • 75
  • Ok. My intention was to treat this simple example with `sourceCpp` as a warm-up before package developing. – KrzJoa Oct 21 '19 at 22:37
  • @KrzJoa One can use `sourceCpp` that way. Did the configuration change for the run-time linker work? Concerning existing packages: arrayfire-r makes a large subset of the API available at R level, but it is not complete. RcppArrayFire does not try to make the API available at R level. Instead it makes it easy to go to C++ where of course the full API is available. This is similar to packages like PcppArmadillo, Rcppeigen and RcppGSL. – Ralf Stubner Oct 22 '19 at 08:48
3

A few things, quickly:

  • when you use sourceCpp() you are not using a package
  • randomly dropping variable used with a package do not help
  • there actually is a package by Ralf whi will probably chime in
  • and we have a writeup about it here at the Rcpp Gallery

So I would probably start by redoing / rebuilding the examples from the Rcpp Gallery article about RcppArrayFire.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • 1
    So, to sum up: 1) `sourceCpp` does not use Makevars and we have to set the flags with `Sys.setenv` 2) You encourage to use RcppArrayFire instead of linking C++ ArrayFire lib on one's own. By the way, I know RcppArrayFire package and I saw the article in the Rcpp Gallery. However, I was trying to compose Rcpp with C++ ArrayFire lib in more "straightforward" way (i.e. without using any additional Rcpp wrappers). I was just trying to learn Rcpp by finding more universal solution, which facilitates to add an arbitrary C++ library (not only the ones already wrapped with Rcpp). – KrzJoa Oct 21 '19 at 08:21
  • 1
    @KrzJoa I am curious what you would see as more "straightforward". If you have any suggestions please don't hesitate to [file an issue](https://github.com/daqana/rcpparrayfire/issues). BTW, are you familiar with https://github.com/arrayfire/arrayfire-r? – Ralf Stubner Oct 21 '19 at 08:51
  • @KrzJoa: Even as a _non-package alternative for simpler tasks_, `sourceCpp` does reflect environment variables. Please the [Rcpp Attributes](https://cloud.r-project.org/web/packages/Rcpp/vignettes/Rcpp-attributes.pdf) vignette for more on it. And also see dozens of other answers here by me and others saying :use a package" when faced with a more involved task, ie one involving system libraries. As Ralf just said again as well. – Dirk Eddelbuettel Oct 21 '19 at 10:46
  • Yes, I know both *RcppArrayFire* and *arrayfire-r*. However, I'm still not sure if they offer access to **full** ArrayFire API. That is the reason I decided to start with bare-metal C++ library instead of using some convenient () wrappers. – KrzJoa Oct 21 '19 at 22:41