Skip to content

Global symbol visibility / deriving from a C++ class defined in a different shared library #452

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fgp opened this issue Apr 18, 2025 · 0 comments

Comments

@fgp
Copy link

fgp commented Apr 18, 2025

In my R package based on cpp11, I defined an interface (on the C++ side) as an abstract base class, i.e. a class that has a bunch of virtual functions. When I implement that interface in the same package (meaning I derive from the base class and override some virtual methods) everything works great.

If users need implementations of that interface beyond what my package provides, they can currently do so purely in R; this works by having one particular implementation of the interface that forwards all calls to user-definable R functions. This works nicely, but it rather slow because it requires a round trip from C++ to R and back to C++ for every invocation.

So it would be nice if users could also use cpp_source() to define their own implementations of the interface directly in C++. For that to work, however, the shared object created by cpp_soure() has to be able to see the symbols for the vtable and RTTI information of the abstract base class. Unfortunately, however, it seems that doing require(my_library) loads the shared library in way that does not make its symbols globally visible. So when I try to implement the interface with cpp_source(), I get an "undefined symbol" error for the RTTI information (i.e. typeinfo) of the abstract base class. This happens even if my code doesn't explicitly use RTTI btw -- the presence of virtual functions is enough for the compiler to generate references to this symbol it seems.

The only workaround I've managed to find for this is to manually load the shared library of my package with dyn.load(..., local=FALSE) before doing library(my_package). So currently I have to tell users to include this rather dirty snippet of code in their scripts:

nnR.dir <- find.package("my_package")
nnR.lib <- if (nzchar(.Platform$r_arch)) {
  file.path(nnR.dir, "libs", .Platform$r_arch, paste0("NEXTNetR", .Platform$dynlib.ext))
} else {
  file.path(nnR.dir, "libs", paste0("my_package", .Platform$dynlib.ext))
}
dyn.load(nnR.lib, local=FALSE, now=TRUE)

It would be great if cpp11 could simplify this somewhat, and e.g. provide a way to export symbols globally the same way that manually loading dyn.load(..., local=FALSE) does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant