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:

FileWho writes itWhat it does
math.ts (your entry)YouImports and calls the function
math.d.tsYouTells TypeScript the function’s signature
math.cppYouThe 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.ts exists 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, where west and CMake pick it up during the build.
  • Native (desktop) — the native toolchain compiles the .cpp files 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++ header

It’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.