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

Open
#452 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
cpp, r

Research direction

Start with the cpp_source() entry point and the package shared-library loading behavior described in the issue, comparing it with dyn.load(..., local=FALSE). Determine how cpp11 could make the abstract base class's vtable and RTTI symbols visible to externally compiled C++ implementations. Done means users can derive from the interface through cpp_source() without manually loading the package library.

Written by the indexing model from the issue text.

Description

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.

Dominant language
C++
Stars
224
Forks
52
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from r-lib/cpp11

All issues in r-lib/cpp11

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.