Quirrel bindings ----------------------------------- Guide in bindings ================= Not full guide but some brief introduction. To get deeper into bindings you need to study samples or read the code of SqRat Sqrat is a C++ binding library designed for the Quirrel scripting language. Sqrat simplifies the process of exposing C++ functions and classes to Quirrel scripts. It automates type conversion between C++ and Quirrel, making it easier to register functions, methods, and classes without needing to manually handle the Quirrel stack in every case. Example ^^^^^^^^^^^^^^^^^^ Basic Includes ^^^^^^^^^^^^^^^^^^ .. code-block:: cpp #include #include Create a module ^^^^^^^^^^^^^^^^^^^^^ SqModules allows to export Quirrel objects (usually tables and classes) as script modules. Example of module creation: :: Sqrat::Table exports(vm); // Create a table to hold the exported functions and classes exports .Func("function_name", function) // Export a C++ function with automatic handling of arguments and return values .SquirrelFuncDeclString(add, "add(a: int, b: int): int", "Returns the sum of two integers, optional string")// Export a C++ function that works with the Quirrel stack, fully documented .SquirrelFunc("function_name", low_level_function) // Export a C++ function that works with the Quirrel stack .SetValue("SOME_ID", value) // Exports is a table, so it can hold any Quirrel value ; module_manager->addNativeModule("module_name", exports); // Expose the table as a module to the Quirrel scripts Simple bind of cpp function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can use Sqrat::Table::Func() or Sqrat::Class::Func() to register a C++ function for use in a Quirrel script. Sqrat automatically handles argument and return value conversions between Quirrel and C++ types. C++: :: Sqrat::Table exports(vm); exports.Func("pow", &std::pow); // Register pow function in the script module_manager->addNativeModule("math_demo", exports); Usage in Quirrel: :: from "math_demo" import pow // or let { pow } = require("math_demo") local result = pow(2, 3) // Calls the C++ std::pow(2, 3) print(result) // Output: 8 Bind function that works with Quirrel stack ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For more control, you can manually handle the Quirrel stack by using SquirrelFunc. This allows you to specify custom logic for processing function arguments and returning values. For instance, you may need this for variadic functions, arguments of varying types, or functions with complex logic that can't be automatically bound. :: SQInteger sum_all_args(HSQUIRRELVM v) { SQInteger n = sq_gettop(v); // Number of arguments SQFloat sum = 0; for (SQInteger i = 2; i <= n; ++i) { // Sum all arguments, starting from 2 (#1 is `this`) SQFloat val; if (SQ_SUCCEEDED(sq_getfloat(v, i, &val))) // For demo purposes, we only handle numbers and ignore other types sum += val; } sq_pushfloat(v, sum); // Push the result return 1; // Number of return values (0 for void, 1 for return value, SQ_ERROR for error) } exports.SquirrelFunc("sum_all_args", sum_all_args, -2); // -2: variable arguments, at least 2 (`this` and a number In script: :: local result = sum_all_args(1, 2, 3, 4) // Calls the C++ function print(result) // Output: 10 Full signature of SquirrelFunc: .. cpp:function:: TableBase& Table::SquirrelFunc(const SQChar* name, SQFUNCTION func, SQInteger nparamscheck, const SQChar *typemask=nullptr, const SQChar *docstring=nullptr, SQInteger nfreevars=0, const Object *freevars=nullptr) :param name: should be string, func should be function that works with Quirrel :param nparamscheck: number of arguments of function. If negative - function can have at least this number of arguments but can accept more. :param typemask: optional typemask (see sq_setparamscheck in :ref:`API `) :param docstring: optional docstring, nfreevars and freevars - free variables of function. :param nfreevars: number of free variables :param freevars: free variables to capture :remarks: The typemask is a string that represent the expected parameter type. The types are expressed as follows: 'o' null, 'i' integer, 'f' float, 'n' integer or float, 's' string, 't' table, 'a' array, 'u' userdata, 'c' closure and nativeclosure, 'g' generator, 'p' userpointer, 'v' thread, 'x' instance(class instance), 'y' class, 'b' bool. and '.' any type. The symbol '|' can be used as 'or' to accept multiple types on the same parameter. There isn't any limit on the number of 'or' that can be used. Spaces are ignored so can be inserted between types to increase readability. For instance to check a function that expect a table as 'this' a string as first parameter and a number or a userpointer as second parameter, the string would be "tsn|p" (table,string,number or userpointer). If the parameters mask is contains fewer parameters than 'nparamscheck', the remaining parameters will not be typechecked. The preferred method for binding C++ functions that work with the Quirrel stack is to use `.SquirrelFuncDeclString`. This declarative method allows you to specify the function signature, argument types (including optional/default values), return type, and documentation string all in one place. **Example:** .. code-block:: cpp .SquirrelFuncDeclString( do_math, "pure do_math(a: int, [b: number = 2]): float", "Performs math operation. Optional argument 'b' defaults to 2." ) This approach allows the engine to: - Parse argument types and generate typemasks for type checking - Reflect metadata for documentation and scripting tools - Recognize pure functions for optimization **Why use SquirrelFuncDeclString?** - **Declarative:** Full signature, types, defaults, and docstring in a single place. - **Robust:** More accurate binding and automatic validation. - **Documentation-friendly:** Signature and docs are automatically extracted. **Deprecation Notice:** The older `.SquirrelFunc` macro is now **deprecated**. It required manually handling arguments from the Quirrel VM stack, which is more error-prone and lacks reflection support. **Always prefer `.SquirrelFuncDeclString` for new bindings.** Bind classes, constants and values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sqrat allows to register C++ classes with member variables and methods that can be accessed from Quirrel scripts. Toy example: :: class Rect { public: float width, height; Rect(float w, float h) : width(w), height(h) {} float area() const { return width * height; } float perimeter() const { return 2 * (width + height); } }; Sqrat::Class rectClass(table.GetVM(), "Rect"); rectClass .Ctor() .Var("width", &Rect::width) .Var("height", &Rect::height) .Func("area", &Rect::area) .Prop("perimeter", &Rect::perimeter) ; exports.Bind("Rect", rectClass); // Bind the class to the table module_manager->addNativeModule("geometry", exports); In script: :: from "geometry" import Rect local r = Rect(1, 3) r.width = 2 print(r.area()) // Output: 6 print(r.perimeter) // Output: 10 SquirrelCtor() may be used for a constructor with a flexible behavior. It has to implement an actual native instance creation. Example: :: static SQInteger rect_ctor(HSQUIRRELVM v) { SQInteger n = sq_gettop(v); if (n == 2) { // copy constructor if (!Sqrat::check_signature(vm, 2)) return sq_throwerror(vm, "Invalid type passed to copy ctor"); } else if (n != 1 && n != 3) return sqstd_throwerrorf(vm, "Invalid arguments count %d", n); Rect *instance = new Rect(0, 0); if (n == 1) // no arguments ; // already initialized with default 0, 0 else if (n == 3) { // instance and 2 numbers SQFloat w, h; sq_getfloat(v, 2, &w); // Should always succeed, because types are specified in type mask (see SquirrelCtor) sq_getfloat(v, 3, &h); instance->width = w; instance->height = h; } else if (n == 2) { // copy constructor (self instance and another instance) Rect *other = Sqrat::Var(vm, 2).value; instance->width = other->width; instance->height = other->height; } Sqrat::ClassType::SetManagedInstance(vm, 1, instance); // Link with script instance return 1; } Sqrat::Class rectClass(table.GetVM(), "Rect"); rectClass .SquirrelCtor(rect_ctor, 0, "x y|n n") // 0 - no argument count check, "x y|n n" - type mask (instance('this') and instance or 2 numbers) .Var("width", &Rect::width) .Var("height", &Rect::height) .Func("area", &Rect::area) .Prop("perimeter", &Rect::perimeter) ; In script: :: from "geometry" import Rect local r1 = Rect(1, 3) local r2 = Rect(r1) local r3 = Rect() Consttable - not documented Class property (setter) - not documented Static functions - not documented