Hot++

C++ script classes

This page describes what a script class can have in a .cpp script.

What a script class can have

At file scope a .cpp script can have:

Static members

A script class can have static data members and static methods. A normal C++ build compiles them as written. hotc moves them out of the class, because the generated class is a state struct with free functions.

A static data member is a module variable. Its name is <Class>__s_<name>. For static int count; in Counter, the name is Counter__s_count. The marker s_ keeps the name apart from the names that hotc generates for the class, such as Counter__plan. So a static member can have the name plan. The module state holds the variable. It migrates by name and type, as any module variable does. The value survives a reload. A new static data member takes its initializer.

hotc accepts these forms of the initializer:

A static method is a free function with the name <Class>__s_<name>. It has no __self. hotc writes its prototype before the class block, so the methods of the class can call it. It writes the definition after the class block. The prototype keeps the default arguments. The definition has none. A static method has the edits of a free function: module variables and static members become hot_mod()->... or the mangled name. A static method that uses this or a member of the base class is an error of the C++ compiler (for example call to non-static member function without an object argument).

hotc rewrites every use of a static member in the file. These forms are valid:

The class name is also a type. See Script classes as types.

A static data member that is const or constexpr is not a module variable. It has no state. hotc writes it as a const at file scope before the class block, with the name <Class>__s_<name>. The uses in the class and outside it use this name. The member needs an initializer in the class. static constexpr int kFactor = 3; becomes constexpr int Counter__s_kFactor = 3;.

When the module block does not exist before the class, a static data member makes the module block at the class. So the types of the static data members must come before the class.

Convert hooks of a static data member

A static method convert_<name> in the class is the hook of the static data member <name>. It works as the free function convert_<var> of a module variable. The generated hook is convert_<Class>__s_<name>. It converts the old value when the type changes. It cannot use a module variable, a static data member or a static method.

class Counter : public Actor
{
    static float count; // was: static int count;
    static float convert_count(int old) { return old + 0.5f; }
};
float Counter::count = 10.0f;

The fixtures tests/scripts/cpp_static_v1.cpp and tests/scripts/cpp_static_v2.cpp show the forms. The second one changes the type of count from int to float.

Pure virtual methods

A method with = 0 is an abstract method. It has a slot and no body. The class is abstract without a keyword. A child class implements the method with override.

class Shape : public Actor
{
public:
    Shape() : Actor("Shape") {}
    virtual int Area() = 0;
    int Twice() { return Area() * 2; }
};

A call of an unimplemented slot throws ScriptError with the text abstract method 'Shape.Area' called. new Shape() is an error of hotc. The host can still create the object. A reload can make a class abstract and back. The fixtures tests/scripts/cpp_abstract_v1.cpp and tests/scripts/cpp_abstract_v2.cpp show them.

Limit: override on a pure virtual method and a definition outside the class are errors (see Unsupported forms).

Definitions outside the class

A script class can declare a method, a constructor or a destructor and define it later in the file, as in plain C++.

class Player : public Actor
{
public:
    Player(int a);
    int hit(int d, int scale = step);
    int get() const;
    int hp = 10;
};

class Other : public Actor { /* ... */ };

Player::Player(int a) : Actor("Player") { hp = a; }
int Player::hit(int d, int scale) { hp -= d * scale; return hp; }
int Player::get() const { return hp; }

The body of the definition has the same edits as a body in the class. It can use a native base member, this, a module variable and a field. The definition can come after other classes. The fixture tests/scripts/cpp_outofclass_v2.cpp shows the forms.

Limit: the body moves into the code of the class. So the body can use only what the class can use. A body that names a type, a function or a constant that the file declares after the class and before the definition is an error of hotc (see Unsupported forms). Plain C++ allows it. Declare such a name before the class. A prototype before the class is enough for a function.

These rules apply:

Member initializer lists

A constructor can have a member initializer list, as in plain C++. Both forms work, f(e) and f{e}, in a constructor in the class and in a constructor that the file defines outside the class.

class Player : public Actor
{
public:
    Player(int hp_, std::string n) : Actor("Player"), hp(hp_ * 2), name(std::move(n)), tag{3} { stamp++; }
    int hp;
    std::string name;
    int tag;
    int stamp = 5;
};

These rules apply:

The fixtures tests/scripts/cpp_ctor_init_v1.cpp and tests/scripts/cpp_ctor_init_v2.cpp show the forms.

Aliases, onReload and convert hooks

These features follow the rules of Hot reload. Only the spelling is C++.