Preface
This post walks you through connecting the VSCode debugger to Rcpp code. It’s a practical guide, drawing from these resources:
- Using VS Code to Debug R Packages with C or C++ Code
- vscode-rcpp-demo (GitHub repo)
- R packages documentation
For reference, I’m using a MacBook Pro (M2 Pro chip) with R 4.3.1 installed via Homebrew.
Step 0: Install Required R Packages
Before diving in, make sure you have these R packages installed:
- vscDebugger : Connects R code to the VSCode debugger
- devtools : For building R packages
- usethis : Helps set up package skeletons
- testthat : For unit testing
Step 1: Set Up VSCode for C++
First, install VSCode if you haven’t already. Then, add these extensions (as recommended by Davis Vaughan):
CodeLLB is the debugger we’ll use to connect to Rcpp.
Step 2: Create an R Package
To debug C++ code with Rcpp, you’ll need to wrap your code in an R package. While it’s tempting to just use an .r
and a .cpp
file together, LLDB can’t easily hook into the C++ code unless it’s part of a package (see this StackOverflow post
).
Here’s a quick example of what you might try without a package:
library(Rcpp)
sourceCpp('my-rcpp-implementation.cpp')
# declare variables
...
cpp_impl(x, y, z)
Unfortunately, LLDB won’t attach to the C++ code in this setup, so packaging is necessary.
You can create a package by:
- Cloning the demo repo above
- Using RStudio (File → New Project → New Directory → R Package with Rcpp)
- Running
usethis::create_package()
in R
Once your package is set up, move your files:
- R files →
R/
directory - C++ files →
src/
directory
Step 3: Configure C++ in VSCode
We will now connect the VSCode debugger so that we can debug the Rcpp code. Depending on your operating system and R installation this step will not be exactly the same, so don’t copy-paste blindly—adapt paths and settings as needed.
Open the command palette (Cmd+Shift+P
), type “edit configurations,” and select the C++ configuration. You might see a UI dropdown like this:
If you choose JSON, you’ll get:
Now, let’s help VSCode find the R and Rcpp header files. Open the command palette (Cmd+Shift+P
), type “edit configurations,” and select the C++ configuration (choose JSON if prompted). You’ll see something like:
{
"configurations": [
{
"name": "Mac",
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-clang-arm64"
}
],
"version": 4
}
In the innermost object we will add the include paths for the R/Rcpp header files:
{
...
"includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/lib/R/4.3/site-library/Rcpp/include/*",
"/opt/homebrew/lib/R/include/*"
]
...
}
Your paths may differ—find them by running these in R:
R.home("include") # Directory with R.h
.libPaths() # Find Rcpp directory
If you run into compilation issues, create a Makevars
file in src/
with:
PKG_CPPFLAGS = -I/opt/homebrew/lib/R/4.3/site-library/Rcpp/include
Adjust the path as needed for your setup.
Step 4: Export Rcpp Functions (NAMESPACE)
The NAMESPACE
file at your package root controls what gets exported. Getting this right is key for debugging.
Manual method:
Add these lines to NAMESPACE
(replace pkgname
with your package name):
useDynLib(pkgname, .registration=TRUE)
importFrom(Rcpp, evalCpp)
exportPattern("^[[:alpha:]]+")
Recommended: Use roxygen2
- In your C++ files (in
src/
), annotate exported functions:
//' @export
// [[Rcpp::export]]
For example:
#include <Rcpp.h>
#include <random>
using namespace Rcpp;
//' @export
// [[Rcpp::export]]
NumericVector f_dens_cpp_1(const NumericVector& y,
const NumericVector& x,
const NumericVector& z){
NumericVector ret(y.length());
for (int i = 0; i < y.length(); i++){
double ans = 1;
for (int j = 0; j < x.length(); j++){
ans *= exp(y[i] * z[j] * x[j] - exp(y[i]*x[j]));
}
ret[i] = ans;
}
return ret;
}
- Run this in your package root to generate Rcpp exports:
Rcpp::compileAttributes()
- Create package documentation:
usethis::use_package_doc()
This creates a file like:
#' @keywords internal
"_PACKAGE"
## usethis namespace: start
## usethis namespace: end
NULL
- Add these lines (again, replace
pkgname
):
#' @importFrom Rcpp evalCpp
#' @useDynLib pkgname, .registration=TRUE
- Finally, generate the
NAMESPACE
:
devtools::document()
Step 5: Write a Unit Test for Debugging
Unit tests are invaluable for debugging. They let you run small, focused pieces of code—perfect for stepping through C++ functions.
From your package root, run:
usethis::use_testthat()
usethis::use_test('my-test')
This creates a testthat/
directory. In your test file, you can call your Rcpp functions directly:
# tests/testthat/test-my-test.R
test_that("Density works", {
dat <- read.csv('2_Poisson.csv')
z <- dat$z
x <- dat$x
step_size <- 0.01
res <- seq(0, 1, step_size)
# f_dens_cpp_1 is an Rcpp function
expect_equal(f_dens_cpp_1(res, x, z), c(0, 1, 2))
})
No need to import the functions—they’re available when you run devtools::test()
.
Step 6: Set Up the Launch Configuration in VSCode
By now, you should have a .vscode
directory in your package root. Add a launch.json
file with:
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) Launch",
"type": "lldb",
"request": "launch",
"program": "/opt/homebrew/Cellar/r/4.3.1/lib/R/bin/exec/R",
"args": [
"--vanilla",
"-e",
"devtools::test()"
],
"env": {
"R_HOME" : "/opt/homebrew/Cellar/r/4.3.1/lib/R"
},
"terminal": "console",
"stopOnEntry": false
}
]
}
If you already have R debugging configs, just add this one to the list. Make sure the "program"
and "R_HOME"
fields match your R installation. Find your R_HOME
by running R.home()
in R. The R binary is usually at $R_HOME/bin/exec/R
(not the script you get from which R
).
Step 7: Debug Your C++ Code
Now, open your C++ file in src/
and set a breakpoint by clicking to the left of the line number:
Go to the debugger tab in VSCode and click the green start button next to your launch configuration. This will start the debugging session:
When your C++ function is called, the debugger will pause at your breakpoint, letting you inspect variables and step through the code:
You can also use the debug console to send commands directly to LLDB.
Final Remarks
That’s it! You now have a working setup for debugging Rcpp code in VSCode. If you want to avoid setting up c_cpp_properties.json
for every project, you can add the include paths globally:
Open user settings as JSON (Cmd+Shift+P
→ “user settings”), and add:
"C_Cpp.default.includePath": [
"/opt/homebrew/lib/R/4.3/site-library/Rcpp/include/*",
"/opt/homebrew/lib/R/include/*"
]
This way, VSCode will always know where to find the R and Rcpp headers for any project.