Hot++

How hotc edits C++

This page describes the edits that hotc makes in a .cpp script: method bodies, lambdas and namespaces.

How hotc edits a body

hotc parses the .cpp file with libclang. It keeps each method body as text. It edits only the names that the generated code cannot resolve:

The generated file keeps the source order of the script. A block at the top declares hot_mod(). The module block comes at the place of the first module variable. The block of a class comes at the place of the class. So a module variable can use only the types and the functions that come before the first module variable. A variable that is not a module variable cannot use a module variable: it gets its value when the library loads, before the module state exists.

All other text stays as you wrote it.

Operators

A script class can have operator members. These operators are allowed:

hotc gives each operator a safe name. For example, operator+= becomes op_add_assign. The slot, the state member and the thunk use this name. A message of the compiler shows the operator again, for example Vec::operator+=.

The shell keeps a C++ operator member. So the code a += b stays as you wrote it, in the script and in native code. The member calls the slot of the written operator. A reload changes the body of the operator.

hotc generates no operator for you. It does not make != from ==. It does not make a compound operator from a binary operator. Write each operator that you need.

A class can have one overload for each symbol. The member is const when the written method is const. The C++ compiler checks the number of parameters of an operator.

An operator that a script class cannot define is an error. See Limits.

Lambdas

A lambda in a method, in a constructor or in a field initializer can use the fields, the methods and the native base members of the object. Use any capture: [this], [=], [&], [=, this], [&, this] or an init-capture such as [self = this]. A generic lambda and a nested lambda work too.

A reload moves the fields of an object to a new place. So a lambda must not keep the address of the fields. hotc makes the lambda keep the object instead. The object keeps its address for all its life. Each use of a field in the lambda finds the fields at the time of the call. A lambda that a field holds sees the new values after a reload.

void Tick(float dt) override
{
    auto bump = [this] { count += 1; Move(Vec3(dt, 0, 0)); };
    bump();
    if (!later)
        later = [&] { return count + 1000; }; // the field "later" is a std::function<int()>
}

The lambda in a field has the code of the version that made it. A reload does not replace the body. The old version stays loaded while a live field can hold code. The next reload does not unload the old version. A lambda that you make again after the reload has the new code. To get the new code, set the field again.

A lambda cannot capture *this. A copy of the object has no meaning in a script. hotc gives an error. Capture a copy of the fields, for example [n = count].

Namespaces

A script class and a module variable can be in a named namespace. The compact form namespace game::ai { ... } works too. A normal C++ build compiles the file as written.

namespace game
{
int score = 0;
int bonus(int x) { return x + score; }

class Player : public Actor
{
public:
    Player() : Actor("Player") {}
    void Tick(float dt) override { score += 1; points = bonus(10); }
    int points = 0;
};
} // namespace game

namespace game::ai
{
class Brain : public Player { /* ... */ };
} // namespace game::ai