Native C++ Code
Sometimes you have a piece of code that’s already written in C++ — a vendor driver, a tight math routine, or a library you’d rather not rewrite in TypeScript. Cuttlefish can call straight into it. You keep the C++ implementation in a .cpp file, and give TypeScript a small declaration file so the transpiler knows the function’s shape.
You don’t learn a new syntax for this. It’s an ordinary relative import — the same one you’d use to split TypeScript across files. Cuttlefish just notices that the module is C++ rather than TypeScript, and treats it as “hand-written, don’t transpile.”
The Three Files
A native module is three files that live next to each other:
| File | Who writes it | What it does |
|---|---|---|
math.ts (your entry) | You | Imports and calls the function |
math.d.ts | You | Tells TypeScript the function’s signature |
math.cpp | You | The actual C++ implementation |
That’s the whole pattern. No decorators, no special keywords.
A Minimal Example
Say you want a hand-tuned square() function in C++.
src/math.d.ts — the declaration
This is a plain TypeScript ambient declaration. It says ”square takes and returns a 32-bit integer,” and nothing more:
export declare function square(x: int32_t): int32_t;src/math.cpp — the implementation
Ordinary C++. You write the body here, exactly as a C++ compiler expects:
#include <stdint.h>
int32_t square(int32_t x) {
return x * x;
}src/main.ts — your TypeScript
You import it with a relative path and call it like any other function:
import { square } from "./math";
export function compute(value: int32_t): int32_t {
return square(value) + 1;
}What gets generated
When you build, Cuttlefish emits the call site and leaves the body to your .cpp. The generated main.cpp references square as an ordinary C++ function call:
#include "main.h"
int32_t compute(int32_t value)
{
return square(value) + 1;
}Note what’s not there: Cuttlefish does not copy math.cpp, does not emit a forward declaration for square, and does not emit an #include for it. Your .cpp is expected to be compiled and linked by the same toolchain that compiles Cuttlefish’s output — exactly as a hand-written C++ source file would be.
How Cuttlefish Knows It’s C++
When you import a relative path like "./math", Cuttlefish looks for two sibling files:
math.d.ts— the TypeScript declaration (gives types)math.cpp— the C++ source (gives the implementation)
If it finds that pair, the module is treated as native — it’s left alone, not transpiled. The .d.ts is the contract between the two languages: it’s what makes square(value) type-check in your TypeScript, while math.cpp is what satisfies the linker later.
If only a
.d.tsexists with no matching.cpp, the import resolves as a plain TypeScript declaration and no native binding is created — the symbols would be declared but never defined, and linking would fail.
Where the .cpp Needs to Live
Cuttlefish does not move your .cpp into the build output. It logs the module as library-managed and leaves it where you put it. So the file has to be somewhere the downstream build already looks:
- Zephyr — under your Zephyr app’s
src/directory, wherewestand CMake pick it up during the build. - Native (desktop) — the native toolchain compiles the
.cppfiles that sit alongside Cuttlefish’s generated output, so place (or build) your native source there.
If a build ever fails to link with undefined reference to square, the .cpp isn’t where the toolchain is looking — that’s the first thing to check.
One More Detail: Headers
You can optionally add a math.h next to your .cpp:
src/
├── math.d.ts ← types for TypeScript
├── math.cpp ← implementation
└── math.h ← (optional) C++ headerIt’s good practice and lets your .cpp stay clean, but Cuttlefish doesn’t require it and won’t emit an #include for it on your behalf. If your .cpp needs its own header, #include it from the .cpp yourself.
When to Reach for This
- A vendor HAL or driver already shipped as C++ that you don’t want to port.
- A control-loop routine you’ve hand-optimized in C++ for timing.
- Reusing an existing C++ library verbatim.
For everything else, writing TypeScript and letting Cuttlefish generate the C++ is simpler — you get type-checking, conflict detection, and AUTOSAR checking for free. Native modules step outside that pipeline: the .cpp you write is not audited by Cuttlefish, so its correctness and compliance are yours to maintain.
On This Page