Assume I have two packages in R, the first named foo
, the second named bar
. I want to include a C function in foo
and share that functionality with bar
in a way that is platform independent and consistent with the CRAN policies.
What is the preferred method of doing this, and how should I go about using function registration and dynamic libraries?
The purpose of my question is that, even though I read through all the documentation I could find, there is nothing that occured to me as the obvious thing to do, and I am unsure what the most sustainable course of action is.
Example:
Assume that in one package foo
, I define a C function addinc
which adds two numbers.
#include <R.h>
#include <Rinternals.h>
SEXP addinc(SEXP x_, SEXP y_) {
double x = asReal(x_);
double y = asReal(y_);
double sum = x + y;
return ScalarReal(sum);
}
In the same package, I can try calling addinc
in an R function named addinr
via the .Call
interface.
addinr <- function(x,y){
.Call("addinc", x, y, PACKAGE="foo")
}
However, when building, checking and installing the package, running addinr
returns the error below, presumably because the function is not yet registered within R.
library(foo)
addinr(1,2)
Error in .Call("addinc", x, y, PACKAGE = "foo") :
"addinc" not available for .Call() for package "foo"
As it seems to me, the easiest way to solve this is to build a dynamic library for the compiled code by adding useDynLib(foo)
to foo
s NAMESPACE file.
This appears to solve the problem because I can now call addinr()
without problems. Moreover, I can run .Call("addinc", ..., PACKAGE="foo")
directly from within R.
My real question, however, occurs when a second package, say bar
, is supposed to use foo
s addinc
. For example, assume that bar
defines a function multiplyinr
as follows.
multiplyinr <- function(x,y){
ans <- 0
for(i in 1:y) ans <- .Call("addinc", ans, x, PACKAGE="foo")
ans
}
This, in fact, works completely fine, and I can call multiplyinr
within R. However, when building and checking bar
, I receive a Note complaining about the fact that bar
is calling foreign language functions from a different package.
Foreign function call to a different package:
.Call("addinc", ..., PACKAGE = "foo")
See chapter ‘System and foreign language interfaces’ in the ‘Writing R Extensions’ manual.
According to this question, the package bar
would not be suitable for submission to CRAN because using .Call()
in this way is not considered "portable" as explained in the Writing R Extensions manual.
In conclusion, the simple solution of having foo
include a useDynLib(foo)
in its NAMESPACE file does not quite seem to cut it. Therefore my question: What is the preferred method to share a C function with other packages?
Moreover:
Is using useDynLib()
truly dangerous or inconsistent with the CRAN policies? What is the purpose of declaring useDynLib()
in the NAMESPACE file as an alternative to registering and building the shared library manually?
Would manually registering the C function and bulding the shared library change anything (i.e., using R_RegisterCCallable()
or R_registerRoutines()
)?
The most basic method for calling C code from R is to use the . C() function described in the System and foreign language interfaces section of the Writing R Extensions manual. Other methods exist including the . Call() and .
In fact, you can take any R script and compile it into a report that includes commentary, source code, and script output. Reports can be compiled to any output format including HTML, PDF, MS Word, and Markdown. The first call to render creates an HTML document, whereas the second creates a PDF document.
Namespaces allow the package writer to hide functions and data that are meant only for internal use, Namespaces prevent functions from breaking when a user (or other package writers) picks a name that clashes with one in the package, and. Namespaces provide a way to refer to an object within a particular package.
To manually submit your package to CRAN, you create a package bundle (with devtools::build() ) then upload it to https://cran.r-project.org/submit.html, along with some comments which describe the process you followed.
The general idea is that the 'native symbol info' objects created by using e.g. useDynLib(<pkg>, <symbol>)
are not part of the public API of a package, and so client packages should not be calling them directly (it's assumed they could be changed in future revisions of the package).
There are two ways to 'export' a compiled routine for use by client packages:
foo
that calls the native routine directly, orR_RegisterCCallable()
/ R_GetCCallable()
pair of functions to get a pointer to the function you want. (Package foo
would call R_RegisterCCallable()
to make some function available; client package bar
would call R_GetCCallable()
to get a pointer to that function)In other words, if a package author 'registers' their C functions, they're declaring that to be part of the public C API of their package, and allow client packages to use / call that through this interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With