Skip to content

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

@fgp

Description

@fgp

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions