{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreid33o3b6ezl2g6mbgfhwuuun2ndkhlccnlutrcxnisr63ukcngwra",
"uri": "at://did:plc:46ti67tc37qcmwp2vaynk6fq/app.bsky.feed.post/3meepats7pu72"
},
"path": "/blog/2026/02/08#chronometre_initial_pair",
"publishedAt": "2026-02-08T19:48:14.983Z",
"site": "http://dirk.eddelbuettel.com",
"tags": [
"concerning R and Python object pointer exchange",
"arrow",
"spdlog",
"Gabi Melman",
"RcppSpdlog",
"R side of the package pair",
"Python side of the package pair",
"pybind11",
"reticulate",
"PyPi",
"CRAN",
"Rcpp mailing list",
"Rcpp Discussions",
"Dirk Eddelbuettel",
"Thinking inside the box",
"sponsor me at GitHub"
],
"textContent": "Both R and Python make it reasonably easy to work with compiled extensions. But how to access objects in one environment from the other _and share state or (non-trivial) objects_ remains trickier. Recently (and while r-forge was ‘resting’ so we opened GitHub Discussions) a question was asked concerning R and Python object pointer exchange.\n\nThis lead to a pretty decent discussion including arrow interchange demos (pretty ideal if dealing with data.frame-alike objects), but once the focus is on more ‘library-specific’ objects from a given (C or C++, say) library it is less clear what to do, or how involved it may get.\n\nR has external pointers, and these make it feasible to instantiate _the same object_ in Python. To demonstrate, I created a pair of (minimal) packages wrapping a lovely (small) class from the excellent spdlog library by Gabi Melman, and more specifically in an adapted-for-R version (to avoid some `R CMD check` nags) in my RcppSpdlog package. It is essentially a nicer/fancier C++ version of the `tic()` and `tic()` timing scheme. When an object is instantiated, it ‘starts the clock’ and when we accessing it later it prints the time elapsed in microsecond resolution. In Modern C++ this takes little more than keeping an internal `chrono` object.\n\nWhich makes for a nice, small, yet specific object to pass to Python. So the R side of the package pair instantiates such an object, and accesses its address. For different reasons, sending a ‘raw’ pointer across does not work so well, but a string with the address printed works fabulously (and is a paradigm used around other packages so we did not invent this). Over on the Python side of the package pair, we then take this string representation and pass it to a little bit of pybind11 code to instantiate a new object. This can of course also expose functionality such as the ‘show time elapsed’ feature, either formatted or just numerically, of interest here.\n\nAnd that is all that there is! Now this can be done from R as well thanks to reticulate as the `demo()` (also shown on the package README.md) shows:\n\n\n > library(chronometre)\n > demo(\"chronometre\", ask=FALSE)\n \n \n demo(chronometre)\n ---- ~~~~~~~~~~~\n \n > #!/usr/bin/env r\n >\n > stopifnot(\"Demo requires 'reticulate'\" = requireNamespace(\"reticulate\", quietly=TRUE))\n \n > stopifnot(\"Demo requires 'RcppSpdlog'\" = requireNamespace(\"RcppSpdlog\", quietly=TRUE))\n \n > stopifnot(\"Demo requires 'xptr'\" = requireNamespace(\"xptr\", quietly=TRUE))\n \n > library(reticulate)\n \n > ## reticulate and Python in general these days really want a venv so we will use one,\n > ## the default value is a location used locally; if needed create one\n > ## check for existing virtualenv to use, or else set one up\n > venvdir <- Sys.getenv(\"CHRONOMETRE_VENV\", \"/opt/venv/chronometre\")\n \n > if (dir.exists(venvdir)) {\n + > use_virtualenv(venvdir, required = TRUE)\n + > } else {\n + > ## create a virtual environment, but make it temporary\n + > Sys.setenv(RETICULATE_VIRTUALENV_ROOT=tempdir())\n + > virtualenv_create(\"r-reticulate-env\")\n + > virtualenv_install(\"r-reticulate-env\", packages = c(\"chronometre\"))\n + > use_virtualenv(\"r-reticulate-env\", required = TRUE)\n + > }\n \n \n > sw <- RcppSpdlog::get_stopwatch() # we use a C++ struct as example\n \n > Sys.sleep(0.5) # imagine doing some code here\n \n > print(sw) # stopwatch shows elapsed time\n 0.501220\n \n > xptr::is_xptr(sw) # this is an external pointer in R\n [1] TRUE\n \n > xptr::xptr_address(sw) # get address, format is \"0x....\"\n [1] \"0x58adb5918510\"\n \n > sw2 <- xptr::new_xptr(xptr::xptr_address(sw)) # cloned (!!) but unclassed\n \n > attr(sw2, \"class\") <- c(\"stopwatch\", \"externalptr\") # class it .. and then use it!\n \n > print(sw2) # `xptr` allows us close and use\n 0.501597\n \n > sw3 <- ch$Stopwatch( xptr::xptr_address(sw) ) # new Python object via string ctor\n \n > print(sw3$elapsed()) # shows output via Python I/O\n datetime.timedelta(microseconds=502013)\n \n > cat(sw3$count(), \"\\n\") # shows double\n 0.502657\n \n > print(sw) # object still works in R\n 0.502721\n >\n\nThe same object, instantiated in R is used in Python and thereafter again in R. While _this_ object here is minimal in features, the concept of _passing a pointer_ is universal. We could use it for any interesting object that R can access and Python too can instantiate. Obviously, there be dragons as we pass pointers so one may want to ascertain that headers from corresponding compatible versions are used etc but _principle_ is unaffected and should just work.\n\nBoth parts of this pair of packages are now at the corresponding repositories: PyPi and CRAN. As I commonly do here on package (change) announcements, I include the (minimal so far) set of high-level changes for the R package.\n\n> #### Changes in version 0.0.2 (2026-02-05)\n>\n> * Removed replaced unconditional virtualenv use in demo given preceding conditional block\n>\n> * Updated README.md with badges and an updated demo\n>\n>\n\n>\n> #### Changes in version 0.0.1 (2026-01-25)\n>\n> * Initial version and CRAN upload\n>\n\n\nQuestions, suggestions, bug reports, … are welcome at either the (now awoken from the R-Forge slumber) Rcpp mailing list or the newer Rcpp Discussions.\n\nThis post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub.",
"title": "Dirk Eddelbuettel: chronometre: A new package (pair) demo for R and Python",
"updatedAt": "2026-02-08T17:10:00.000Z"
}