Hot++

Module level

The top of a script file can hold imports, enums, structs, variables and native stubs.

Imports

using "x.h"; includes a native header. using <x>; includes a standard header. using "x.hot"; loads another script. A using can stand anywhere at module level, also after a class.

using "parent.hot";            // script
using "Actor.h";               // native header
using <algorithm>;             // standard header

class Grunt : Unit {
    Grunt(int r) : base(r + 1) { }
}

using <map>;                   // allowed here too

These headers are always included, so they need no using: <cstddef>, <cstdint>, <cstdio>, <cstdlib>, <cstring>, <functional>, <memory>, <mutex>, <new>, <stdexcept>, <string>, <tuple>, <type_traits>, <typeinfo>, <unordered_map>, <utility> and <vector>. Any other standard header (<map>, <optional>, <algorithm>) needs a using.

Namespaces

using name; makes the names of a native namespace visible. It becomes using namespace name; in the C++ text. A . in the name is a ::. The line is at file scope, after all the includes, so it is valid for all the code of the script. The type lookup of the translator sees the names too.

using "game.h";
using game.ai;                 // using namespace game::ai;
using std;                     // the standard namespace (see the module attribute std)

class Grunt : Unit {
    Brain brain;               // game::ai::Brain
}

The name std follows the configured standard namespace, as std. does in a type.

Type aliases

using Name = type; gives a new name to a type. It becomes using Name = type; in the C++ text. The name works everywhere a type name works: fields, parameters, return types, locals, template arguments, new and as. Before a . it is a scope, so an alias of an enum can name the values (Color2.Red).

using Ints = std.vector<int>;      // a native type
using Soldier = $Recruit;          // a script class: a handle, like $Recruit

class Platoon {
    using Count = int;             // in a class body: for this class only
    Ints nums;
    Soldier lead;                  // a handle: null at the start
    Count n;
}

Enums and structs

An enum is a scoped enum. Add a base type after :. The default base type is int. A struct is a value type. A bare class name is a value, and $C is a handle: see Value uses.

enum Mode { Idle, Walk, Run }
enum Small : byte { A, B = 5, C }

struct Point {
    float x = 0.0;
    float y = 0.0;
}

Module variables

A variable at file level is shared by all classes of the file. A reload keeps its value.

using "Actor.h";

enum Mode { Idle, Walk, Run }

int total = 0;
Mode mode = Mode.Idle;

class Hero : Actor {
    override void Tick(float dt) {
        total++;
        mode = Mode.Run;
    }
}

An initializer can use only a variable that is above it. A later variable, or the variable itself, is an error. An initializer cannot call a function, a free function or a static method. The error is a module variable initializer cannot call the function '<name>'. The name HotModule is reserved.

[alias("old")] before a variable keeps the value of the old name.

[alias("total")] int sum = 0;

A module variable T* of a native class with a weak source is a hot::CheckedPtr<T>, as a pointer field is. It is weak, it reads null after the object dies, and it keeps its target across a reload. The same errors apply as for a field. See “Pointer fields” in Borrowing.

using "Actor.h"; using "Equip.h";

Charm* last_charm = null;       // a checked pointer

class Hero : Actor {
    override void Tick(float dt) {
        if (last_charm != null) last_charm.Bump(1);
    }
}

Free functions

A function at file level has no class. Write the return type, the name and the body.

using "Actor.h";

int Add(int a, int b) {
    return a + b;
}

int Scale(int x, int k = 3) {
    return x * k;
}

class Hero : Actor {
    int sum = 0;

    override void Tick(float dt) {
        sum = Add(2, Scale(4));
    }
}

A free function can have default parameter values. It can call itself and it can call other functions. It can read and write a module variable. It has no this.

The translator writes a free function as a plain C++ static function with the name hot_fn_<name>. It has no slot, no thunk and no manifest entry. A reload replaces the code of the module. The next call runs the new body.

Rules:

A static method of a class is a function of the class. See Static methods.

Native stubs

A stub tells the translator how a native type behaves. handle makes it a shared handle. value makes it a copied value.

using "Actor.h";
using "Weapon.h";

[native, handle("std::shared_ptr<$T>")] class Weapon;
[native, value] class std.string;

A stub can cover only the subclasses of a type, and cpp_prelude adds C++ text: see “Scope of a stub” in native-interop-scope.md.

A convert hook (T convert_<var>(Old old)) is a special function at file level. A convert hook cannot read a module variable. An imported script cannot have module variables, convert hooks or free functions.