-
Notifications
You must be signed in to change notification settings - Fork 0
Members and Access
A Haxe private member maps to C++ protected, not private: Haxe private is accessible from
subclasses (and Haxe has no "private even from subclasses" concept), so emitting C++ private would
reject an inherited-member access that Haxe accepts. Hatchet therefore never emits C++ private —
hidden members are protected (still closed to outside code, open to subclasses, matching Haxe).
Every pair of default/null/never (pure access control) is lowered to direct field access, the
backing field hidden as protected behind a generated GetX for (default,null).
A generated getter over a container (Array/Map → std::vector/std::map) or a value-struct
field returns a mutable reference (T&), not a const T copy, so Haxe's reference-type mutation
through the getter works: obj.items[k] = v; and in-place struct mutation compile and take effect.
(A scalar getter still returns a const T value.)
Custom accessors with real Haxe routing — a user-written get_x/set_x is emitted as a real
method and every access routes through it, external and internal alike, except inside the
property's own accessors (Haxe's recursion rule): reads become get_x(), writes become set_x(v) —
including constructor writes, and compound writes/++/--, which desugar exactly as Haxe does
(x += v → set_x(read + v), with a side-effecting receiver hoisted so it evaluates once); an
accessor whose signature omits its return type gets the property's type, as Haxe infers it (so
function set_x(x:Float) { return this.x = x; } is a double-returning function, not a value
return from void).
Per Haxe physicality, a non-@:isVar (get,never) emits no backing field at all. A set property
without a set_x keeps the Hatchet dialect: an auto-generated trivial SetX (with the
value-vs-pointer const rule) and direct internal writes.
For owned pointer fields behind a custom setter, the setter's direct store is the single
delete-before-overwrite site (routed callers never also free), and a setter that returns the field
reads to the escape analysis as the value being handed out — the field then leans borrowed (leak over
double-free, the standard bias; @owned opts the destructor in). See Memory Ownership.
(get, default) and dynamic access remain flagged as unsupported (see Diagnostics).
A static field is class-scoped, not a per-instance member — it is never a this-> field. How it
is emitted depends on its type and initializer:
-
Scalar /
Stringwith a literal (or no) initializer — a plain class static:static T NAME;in the header, with an out-of-lineT Class::NAME = <literal>;definition. Reads areClass::NAME. -
Anything else — a Meyers singleton. This covers any struct / container / reference-typed
static (C++98 cannot constant-initialise one as a class-scope data member) and a scalar with a
non-literal initializer (a call,
new, arithmetic, …). The field becomes astatic T& NAME()accessor whose function-localstaticholds the value, initialised on the first call. Afinalfield returnsconst T&(immutable); avarstays writable through the reference. Reads areClass::NAME().
The Meyers form defers the initializer to first use instead of running it at an unspecified point
in the C++ static-initialisation order (the static init order fiasco), which matters because such
a static runs real code — e.g. static var Identity:Matrix = FillMatrix([...]). A function-local
static is initialised exactly once by the language, so no guard flag is needed; when the
initializer builds a temporary (such as the array here) that setup is folded into a one-off
_init_* helper so it too runs once — the same idiom as a file-scoped Array final's builder.
Reads resolve the same way whether written bare inside the class or qualified as Class.NAME from
another class.
Because the accessor-vs-data-member choice is driven by the field's type, a consuming extern /
@proxy binding — which carries no initializer — reads the field the same way
the producing class emits it. A struct-typed static bound through an extern is called
(native::Class::NAME()), matching the native Meyers accessor; a scalar static final stays a plain
data-member read.
-
@libexport— export a class from the shared library via the portable<PREFIX>_CLASSvisibility macro (was@:declbefore v0.2.7; see Metadata). -
@:overload(...)— a call is resolved to the matching C++ overload by argument type, else a hard error. -
@cexport—extern "C"export of a module-level function via a portable macro (was@:abibefore v0.2.7). -
abstract classandabstract function— an abstract method becomes a pure virtualvirtual T f() = 0;, declared and never defined. - The base-from-member
Holderidiom for constructors whosesuper(...)is not the first statement.
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