Foreign Function Interface Overview#

Overview#

BridgeStan works by wrapping the Stan Model class generated by the Stan compiler in a C-compatible interface which exposes the desired functionality. BridgeStan does this by using the extern "C" linkage available in C++ to expose functions which are callable from C and C-compatible sources.

BridgeStan clients are then built around their language’s Foreign Function Interface (FFI).

This is a fairly portable solution, since the subset of the C language used in the bindings is small, and most major platforms employ one of the same C calling conventions, namely the “cdecl” convention. This allows the programs, even when they are compiled by different compilers, to talk to each other in an agreeable way, placing arguments and return values in standard locations for communication between languages.

Each of the BridgeStan clients is built around the C-compatible FFI provided by the host language. By sticking to a simple, C-level API, we can avoid writing language specific code required by higher level FFIs such as pybind or Rcpp. These provide additional functionality and somewhat “nicer” bindings, but at the cost of making the source code specific to the language you want to interface with.

Language-specific Notes#

Python#

The Python FFI documented in the standard library module ctypes.

Our usage is standard with one exception, which is we use the CDLL interface on all platforms, even Windows, due to the fact that BridgeStan models are compiled with MingGW’s gcc.

The NumPy module numpy.ctypeslib is also used for compatibility.

Julia#

Julia’s FFI is documented in the Julia manual.

R#

R features several built-in forms of foreign function interface. We use the most basic one, called .C, as this is the least dependent on R’s internals.

Documentation on the .C interface can be found by calling help(.C) in an R session. Equivalent documentation is available online, as well as an extended walk-through.

Note: One quirk of the .C interface is the requirement that all inputs and return values are passed by pointers. This is the reason for the bridgestan_R files in the source.

General Problems#

Allocated Memory#

Generally speaking, memory allocated on one side of a language barrier must also be freed on that side. This means special consideration is needed to pass strings back and forth between the languages, and inspired some of the design decisions behind ideas like returning the parameter names as a comma separated list, rather than the more “natural” array of strings.

Output Streams#

Printed output from the C++ code cannot easily be captured in the higher-level language. This is particularly relevant for error messaging, which is printed to the standard error output stderr from C++. This does not, for example, correspond to the sys.stderr stream available from Python.