Luau Bridge

Introduction

A Luau bridge is a representation of a static class in a Luau environment which has its methods mapped to C++ functions. This is the entry point for interopability between C++ and Luau.

Creating a Bridge

To create a bridge, you must first create a new class in C++ which inherits from LuauBridge<T>:

class MyNewBridge : public LuauBridge<MyNewBridge> {
public:
    MyNewBridge() : LuauBridge("MyNewBridge") {
        // Register methods here
        this->seal();
    }
};

The LuauBridge constructor takes a single argument, the name of the class. This is used to define the table counterpart of the bridge instance in the Luau environment.

Warning

The seal method must be called at the end of the constructor for end-user bridges. This method is used to expose the bridge to the Luau environment, and omitting it will result in the bridge not being accessible from Luau.

The point in this mechanism is to be able to do inheritance from the C++ side, and the seal method is what allows end-classes to be created.

Exposing It

To expose the newly created class, you need a reference to a Luau context and the actual bridge object:

#include <bboc/engine/scripting/Luau.hpp

Luau luau;
MyNewBridge myNewBridge(luau);

Note

Bridge objects must have their lifecycle handled by the caller. This means that the bridge object must be kept alive for as long as the Luau context is accessible.

Mapping Methods

Methods are mapped to the bridge by using the map method:

class MyNewBridge : public LuauBridge<MyNewBridge> {
public:
    MyNewBridge() : LuauBridge("MyNewBridge") {
        this->map("myMethod", &MyNewBridge::myMethod);
        this->seal();
    }

private:
    int myMethod() {
        // Argument checking -- scroll down to see how this can be simplified
        if (this->l().getTop() != 1) {
            this->l().error("Expected 1 argument");
            return 0;
        } else if (!this->l().isString(1)) {
            this->l().error("Expected string");
            return 0;
        }

        std::string_view myString = this->l().toString(1);
        
        // Do something with myString

        // Return the same string back
        this->l().pushValue(1);
        return 1;
    }
};

The map method takes two arguments: the name of the method in Luau, and a pointer to the method in C++. The method must return an integer, which is the number of return values pushed to the stack.

Using Arguments

All of the arguments are part of the stack just like with regular Luau functions.

For example, if we do the following in a script:

local myString = MyNewBridge.myMethod("Hello, world!")

The Hello, world! string will be pushed to the stack in its first #1 position.

Note

If you use the : operator in Luau (e.g. MyNewBridge:myMethod("Hello, world!")), the first argument will be the static class itself.

Simplifying Argument Checking

The BBOC_LUAU_ARGCHECK() macro can be used to simplify strict argument type checking. This macro takes in the name of the bridge class, and then a set of types that defines the positional arguments of the function.

int MyNewBridge::myMethod() {
    auto values = BBOC_LUAU_ARGCHECK(MyNewBridge, std::string_view);

    std::string_view myString = std::get<1>(values);
    
    // Do something with myString

    // Return the same string back
    this->l().pushValue(1);
    return 1;
}

This will automatically check if the first argument is a string, and if not, it will throw a Luau error and return 0.

Returning Values

Returning values is done by pushing them to the stack, and then returning the number of values pushed.