The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
dr_*_cpp)The dr_*_cpp() family registers a route whose handler is
a C function exported by another R package. Drogon’s
worker threads call the handler directly — the request never reaches the
R main thread, so nothing in the hot path acquires the R
interpreter.
This is the only variant where R is not in the loop. It’s intended for inference packages whose work is already in C/C++ (ggmlR, llamaR, sd2R, embedding/classifier packages) and that want to serve HTTP without paying the R round-trip per request.
For the request shape and overall picture see
vignette("drogonR", package = "drogonR").
The header lives in drogonR’s installed include directory:
$(R_HOME_DIR)/library/drogonR/include/drogonR.h
A package using it adds drogonR to LinkingTo: so R’s
build machinery puts that directory on the compiler’s -I
path:
Package: yourPackage
Imports: drogonR
LinkingTo: drogonR
The signature every dr_*_cpp() handler must match:
#include <drogonR.h>
typedef int (*drogonr_unary_handler_t)(
const char *body, size_t body_len,
const char *query,
const char *const *path_params, size_t path_params_n,
const char *const *headers, size_t headers_n,
char **out_body, size_t *out_len,
int *out_status,
char **out_content_type);body / body_len — request body bytes; not
NUL-terminated.query — raw query string (everything after
?), or NULL.path_params[i] — captured route parameters in the order
they appear in the path pattern ("/items/:id/sub/:slug"
gives path_params[0] = "<id>",
path_params[1] = "<slug>").headers[2*i] / headers[2*i+1] — flat
(name, value) pairs. Drogon lowercases header
names, so match against "x-trace", not
"X-Trace".drogonR owns every input pointer; they are valid for the duration of the call and must not be retained.
The handler writes:
*out_body — malloc()’d response body
(drogonR free()s it after sending). NULL is
allowed if *out_len == 0.*out_len — number of bytes in
*out_body.*out_status — HTTP status code.*out_content_type — optional malloc()’d
MIME string. If left at the initial NULL, drogonR sends
application/octet-stream.Return value: 0 on success, non-zero to signal failure
(drogonR sends a generic 500 and free()s
*out_body / *out_content_type if the handler
allocated them before bailing out).
This is the test backend drogonR uses internally
(inst/test-backend/drogonRtestbackend/src/backend.c),
trimmed to the two routes the bench uses.
#include <drogonR.h>
#include <R.h>
#include <R_ext/Rdynload.h>
#include <stdlib.h>
#include <string.h>
static char *dupbytes(const char *data, size_t n) {
char *out = (char*) malloc(n > 0 ? n : 1);
if (out && data && n > 0) memcpy(out, data, n);
return out;
}
static char *dupcstr(const char *s) {
size_t n = strlen(s);
char *out = (char*) malloc(n + 1);
if (out) memcpy(out, s, n + 1);
return out;
}
/* /ping — fixed JSON {"ok":true} */
static int h_ping_json(const char *body, size_t body_len,
const char *query,
const char *const *path, size_t path_n,
const char *const *hdrs, size_t hdrs_n,
char **out_body, size_t *out_len,
int *out_status, char **out_content_type) {
static const char k[] = "{\"ok\":true}";
*out_body = dupbytes(k, sizeof(k) - 1);
*out_len = sizeof(k) - 1;
*out_status = 200;
*out_content_type = dupcstr("application/json");
return 0;
}
/* /echo — echo the body back as text/plain */
static int h_echo(const char *body, size_t body_len,
const char *query,
const char *const *path, size_t path_n,
const char *const *hdrs, size_t hdrs_n,
char **out_body, size_t *out_len,
int *out_status, char **out_content_type) {
*out_body = dupbytes(body, body_len);
*out_len = body_len;
*out_status = 200;
*out_content_type = dupcstr("text/plain; charset=utf-8");
return 0;
}
void R_init_yourPackage(DllInfo *dll) {
R_RegisterCCallable("yourPackage", "ping", (DL_FUNC) h_ping_json);
R_RegisterCCallable("yourPackage", "echo", (DL_FUNC) h_echo);
R_useDynamicSymbols(dll, FALSE);
}R-side wiring — note that registration is eager:
drogonR resolves the symbol via R_GetCCallable() at
dr_*_cpp() time, so a typo surfaces immediately, not on the
first request.
library(drogonR)
app <- dr_app() |>
dr_get_cpp ("/ping", package = "yourPackage", callable = "ping") |>
dr_post_cpp("/echo", package = "yourPackage", callable = "echo")
dr_serve(app, port = 8080L, threads = 4L)The four registration helpers are dr_get_cpp,
dr_post_cpp, dr_put_cpp,
dr_delete_cpp. All four take
(app, path, package, callable).
Native handlers run on Drogon’s worker thread pool, not on the R main thread. They MUST NOT:
<Rinternals.h>
(Rf_*, PROTECT, R_alloc, …)SEXPRf_eval, no
R_tryCatch, …)R is single-threaded; doing any of the above from a worker thread is undefined behaviour, typically a crash you’ll see only under load.
Configuration that requires R (loading models, reading args, building
caches) belongs on the R side, before dr_serve() is called.
Pass the result to your C handlers through whatever your package already
uses internally — globals, an opaque pointer in
R_ExternalPtrAddr(), etc.
| Pointer | Allocated by | Freed by | Lifetime |
|---|---|---|---|
body, query |
drogonR | drogonR | duration of the call |
path_params[i] |
drogonR | drogonR | duration of the call |
headers[i] |
drogonR | drogonR | duration of the call |
*out_body |
handler (malloc) |
drogonR | until response sent |
*out_content_type |
handler (malloc) |
drogonR | until response sent |
If the handler returns non-zero, drogonR still free()s
any allocated out-pointers — so it is safe to allocate them before
discovering the failure path, no leak.
dr_*_cpp() with dr_get() /
dr_post() on the same app. The slow / configuration / admin
endpoints can stay in R, the hot inference endpoint goes through C.For the plumber drop-in, see
vignette("mode-plumber-shim", package = "drogonR"). For
R-side handlers, see
vignette("mode-native", package = "drogonR").
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.