Guide
Calling a C++ Function from Carbon and Exposing Carbon Types to C++
Guide to calling a C++ function from Carbon and exposing Carbon types to C++ using the Carbon toolchain and Bazel.
Published by Tasadduq Burney
14 Jan 2026, 20:07 UTC
3 min120.8K views0

Desired outcome
Enable a Carbon module to call an existing C++ function (e.g., a math utility) and, if needed, expose Carbon-defined types to C++ callers without rewriting the legacy library.
Prerequisites
- Carbon toolchain installed: clang‑based Carbon compiler,
carbon-build, and the Carbon standard library. - A C++ compiler compatible with the toolchain (GCC 12+ or Clang 15+).
- Bazel available for building mixed‑language targets.
- Basic familiarity with writing
.carbonfiles and BazelBUILDfiles.
Procedure
- Create the C++ header and source.
// math.h #ifndef MATH_H #define MATH_H int add(int a, int b); #endif
Place these files in your project directory, e.g.,// math.cpp #include "math.h" int add(int a, int b) { return a + b; }./src/math.hand./src/math.cpp. - Write a Carbon file that imports the C++ declaration.
The// main.carbon import "math.h" // imports the C++ header fn main() { let result: i32 = add(2, 3); print(result); }importdirective tells the Carbon compiler to treat the header as a C++ declaration accessible via theextern "C++"ABI. - Add any Carbon‑side wrappers (optional).
If you need to expose a Carbon struct to C++, define it in a Carbon file and mark it with
extern "C++":
This generates a C++‑compatible layout for// point.carbon extern "C++" struct Point { x: f64, y: f64, }; fn distance(p: Point) -> f64 { return (p.x * p.x + p.y * p.y).sqrt(); }Point. - Update the Bazel
BUILDfile.
Thecc_library( name = "math_lib", srcs = ["src/math.cpp"], hdrs = ["src/math.h"], visibility = ["//visibility:public"], ) carbon_library( name = "main_carbon", srcs = ["main.carbon"], deps = [":math_lib"], ) cc_binary( name = "demo", srcs = [], deps = [":main_carbon"], )carbon_libraryrule depends on thecc_libraryso that Bazel builds the C++ code first and links it with the Carbon output. - Build and run.
From the workspace root run:
Then execute the produced binary:carbon-build build //:demo
You should see the outputbazel-bin/demo5printed to the console.
Expected checks
- Compilation finishes without linker errors.
- Running the binary produces the expected result (e.g.,
5for theaddexample). - To confirm Carbon‑to‑C++ data flow, inspect symbols:
nm bazel-bin/demo | grep addshould show a reference to the C++addsymbol. - If you exposed a Carbon struct, write a small C++ test that includes the generated header and accesses the struct’s fields; the program should compile and run.
Recovery options
- Linking failures. Verify that the C++ library was built with the same ABI as the Carbon compiler expects (Itanium ABI). Rebuild the C++ code with
-fPICand, if exceptions are not needed, add-fno-exceptionsto match Carbon’s default. - Version mismatch. Ensure the Carbon compiler version corresponds to the C++ ABI version used by your toolchain; consult the Carbon release notes for ABI compatibility notes.
- Missing symbols. Run
objdump -t bazel-bin/demoornmto see undefined symbols; check that thecc_librarytarget is correctly listed in thedepsof thecarbon_library. - Debugging. Enable debug symbols for both languages (
--copt=-gfor C++ and--carbon_opt=-gfor Carbon) to allow source‑level stepping in a debugger likelldborgdb.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.