Calling C++ from Carbon: Packages, Extern Blocks, and Common Pitfalls
Learn how to call existing C++ functions from Carbon by declaring a package, using an extern "C++" block, and following best practices to avoid common pitfalls. The guide includes a step‑by‑step example and a practical checklist.
17 Nov 2025, 07:57 UTC

Use Carbon to Call Existing C++ Code with a Clear Package Declaration
When you want to reuse a mature C++ library in a new Carbon project, the most reliable path is to wrap the C++ symbols inside a Carbon extern "C++" block and keep the package layout explicit. The pattern looks like this:
package Demo.Math
import Core;
extern "C++" {
fn Add(a: i32, b: i32) -> i32;
}
pub fn add(a: i32, b: i32) -> i32 {
Add(a, b)
}
Compile with the Carbon driver (carbonc) and link against the C++ object files. The compiler generates a C++ symbol lookup for Add and the resulting binary works as expected.
Why Explicit Packages and Imports Matter
Carbon requires every source file to start with a package statement, e.g., package Demo.Math. This rule forces a flat, dependency‑graph‑aware namespace and eliminates the accidental name clashes that plague C++ header‑only code. Importing other Carbon packages is explicit: import Core; pulls in the standard library definitions without polluting the global scope.
Step‑by‑Step: Building a Carbon Wrapper for a C++ Function
- Create the C++ implementation. Save
add.cpp:extern "C++" int Add(int a, int b) { return a + b; } - Compile the C++ file to an object. Run on the command line:
g++ -c add.cpp -o add.o - Write the Carbon wrapper. Save
demo_math.cbnwith the example code above. - Compile the Carbon file. Use the Carbon driver:
carbonc demo_math.cbn -o demo_math.o - Link everything together. Build the final executable:
g++ demo_math.o add.o -o demo_math - Run and verify. Execute
./demo_mathand ensure the output matches the expected sum.
Key points to verify:
- The Carbon compiler must emit a symbol named
Addwith C++ mangling. Inspect the object withnm demo_math.ofor the symbol. - The C++ compiler must accept the
extern "C++"declaration. If you see a mangling mismatch, adjust the extern block to useextern "C"and provide a C wrapper.
Limitations of Carbon’s C++ Interop
- Experimental Toolchain. Current releases of
carboncare not yet production‑ready; build reproducibility may vary. - Type Mapping Restrictions. Only a subset of C++ types are directly mappable: primitive integers, floating‑point, and pointers to opaque structs. Complex templates, overloads, and reference qualifiers are not yet supported.
- ABI Assumptions. Carbon assumes the C++ compiler uses the same ABI for the same language version. Mixing GCC and Clang objects may produce link errors.
- Build Integration. Your build system must invoke both
carboncand a standard C++ compiler. There is no automatic dependency tracking yet.
Common Mistakes and How to Avoid Them
- Missing the Package Declaration. Without
package, name resolution fails and the compiler emits errors about undefined identifiers. - Assuming One‑to‑One Type Mapping. Carbon’s
Stringdoes not map to a C++std::stringautomatically. Useextern "C"wrappers for complex types. - Using Carbon‑Only Features in Extern Signatures. Borrows, unique ownership, and other ownership semantics are not allowed in an
extern "C++"block; only C‑compatible types may appear. - Ignoring Symbol Mangling. The name
Addwill be mangled according to the C++ compiler’s rules. If the Carbon compiler emits a different mangling, the linker will fail.
Practical Checklist Before You Build
- Confirm the Carbon language version you are using (
carbonc --version). - Verify that the C++ compiler’s ABI matches the one expected by Carbon.
- Run
nm add.o | grep Addto see the mangled symbol name and compare it to the name emitted bycarbonc -dump-symbols demo_math.cbn. - If you hit a linker error, try wrapping the C++ function in a C extern block and call that from Carbon instead.
By following the explicit package and extern conventions, you can incrementally introduce Carbon into a C++ codebase while keeping the build stable and the namespace clean.
Conclusion
Carbon’s package system and extern "C++" interop provide a straightforward, if experimental, path to reuse existing C++ code. Keep the package header explicit, map only supported types, and be mindful of ABI and mangling. With a clear build recipe, you can safely call into C++ from Carbon and gradually migrate parts of your project to the new language.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.