-
Notifications
You must be signed in to change notification settings - Fork 0
Interop via @proxy
A native C++ class can be consumed (you hold a handle and call its methods) or produced (you write
a subclass of it). hxcpp does the first directly, but it cannot make a Haxe class subclass a
non-hxcpp C++ base — a managed, garbage-collected object can't also be a foreign C++ object (the same
reason JNI never lets a Java class extends a native one). @proxy covers both: a glue type that is
never emitted, identified by a mandatory fully-qualified native class name that must match a
declared extern.
Transparent glue: every reference transpiles as the native type, so calls pass straight through. The stub bodies exist only so hxcpp type-checks.
@:include("native.h") @:native("ns::INative") @:structAccess
extern class INative { public function Run():Int; }
@proxy("ns::INative")
abstract Handle(cpp.Pointer<INative>) {
public function Run():Int { return 0; } // stub — Hatchet dispatches to the extern
}
// a field `h:Handle` lowers to `ns::INative* h`, and `h.Run()` to `h->Run()`A base your code subclasses: the proxy itself is not emitted, but extends Name becomes
: public ns::IBase and a super(...) call routes to the native constructor. This is the only way to
subclass a native C++ base.
@:include("native.h") @:native("ns::IBase") @:structAccess
extern class IBase {}
@proxy("ns::IBase")
abstract class Base {
public function new(id:Int) {}
public abstract function OnTick():Void;
}
class Thing extends Base { // -> class Thing : public ns::IBase
public function new() { super(7); } // -> : ns::IBase(7)
public function OnTick():Void {}
}Each a hard error otherwise:
- the fully-qualified native class name is a mandatory string argument (
@proxy("ns::IBase")); - it must name a type declared
externwith a matching@:native(thatexternalso supplies the@:includepulled into emitted subclasses); - it applies only to an
abstractnewtype (consume) or anabstract class(produce).
A proxy may share its leaf name with an unrelated native type — e.g. a ui.Vertex consume proxy
(→ ui::Vertex*) alongside a gfx.Vertex value struct (@:native("gfx::Vertex") extern typedef), with
a module importing both. That is safe: an unqualified Vertex in your own code follows Haxe's normal
import resolution (qualify it — gfx.Vertex — when you mean the other one), but the member types of
gfx's own typedefs (Mesh.vertices:Array<Vertex>, Line.a:Vertex) always resolve in gfx, so
indexing, field access, and nested struct literals keep the struct's value semantics rather than
picking up the proxy's pointer. Types recovered from a C++ spelling (a vector element, a map value)
are likewise matched on the fully-qualified name. See Types & Nullability.
For a produce proxy, keep the Haxe member surface within what the native base actually exposes to
subclasses (C++ protected/public): a field that is private in the native base would type-check
under hxcpp but be rejected by the C++ compiler in an emitted subclass.
See Metadata for the full metadata split and the extern keyword.
Hatchet is licensed under the MIT License — see LICENSE. (c) 2026 Andrew Grant Lind
Getting Started
Language Support
- Declarations
- Value Types & Abstracts
- Members & Access
- Statements & Expressions
- Types & Nullability
- Conditional Compilation
- Memory Ownership
Semantics & Interop
Internals