Luwra does not provide a standalone version of Lua nor does it isolate its features. This means that all functions and classes operate on lua_State (or the alias State). Doing this allows you to integrate Luwra however you like.
Although Luwra provides a variety of features, its main concern is efficient and safe interaction with the Lua stack.
A fundamental aspect of this is the abstract template Value. Every type which can be pushed onto or read from the stack has a specialization of it. Useful implementations are provided out of the box:
| C++ type | Pushable | Readable | Lua type |
|---|---|---|---|
| bool | yes | yes | boolean |
| signed char | yes | yes | number (integer since 5.3) |
| signed short | yes | yes | number (integer since 5.3) |
| signed int | yes | yes | number (integer since 5.3) |
| signed long int | yes | yes | number (integer since 5.3) |
| signed long long int | yes | yes | number (integer since 5.3) |
| unsigned char | yes | yes | number (integer since 5.3) |
| unsigned short | yes | yes | number (integer since 5.3) |
| unsigned int | yes | yes | number (integer since 5.3) |
| unsigned long int | yes | yes | number (integer since 5.3) |
| unsigned long long int | yes | yes | number (integer since 5.3) |
| float | yes | yes | number |
| double | yes | yes | number |
| long double | yes | yes | number |
| const char* | yes | yes | string |
| std::string | yes | yes | string |
| std::nullptr_t | yes | yes | nil |
| std::tuple<T> | yes | no | depends on the tuple contents |
| lua_CFunction | yes | no | function |
| NativeFunction<R(A...)> | no | yes | function |
| FieldVector | yes | no | table |
Note: Some numeric types have a different size than their matching Lua type - they will be truncated during push or read operations.
When pushing values onto the stack you can either use Value<T>::push or the more convenient push.
// Push an integer
luwra::push(lua, 1338);
// Push a number
luwra::push(lua, 13.37);
// Push a boolean
luwra::push(lua, false);
// Push a string
luwra::push(lua, "Hello World");
// Push a table
luwra::push(lua, luwra::FieldVector {
{"one", 1},
{1, "one"},
{"nested", luwra::FieldVector {
{"more", "fields"}
}}
});
This produces the following stack layout:
| Absolute Position | Relative Position | Value |
|---|---|---|
| 1 | -5 | 1338 |
| 2 | -4 | 13.37 |
| 3 | -3 | false |
| 4 | -2 | "Hello World" |
| 5 | -1 | {one = 1, [1] = "one", nested = {more = "fields"}} |
It is possible to provide a template parameter to push to enforce pushing a specific type.
In most cases you are probably better off by letting the compiler infer the template parameter.
Simple retrieval of Lua values is done using read<T>. Consider the stack layout from the previous example. This is how you would retrieve a value from the stack.
// Retrieve the integer at position 1
int value = luwra::read<int>(lua, 1);
// Similiar with a relative index
int value = luwra::read<int>(lua, -5);
What happens when a value which you are trying to read mismatches the expected type or cannot be
converted to it? Most Value<T> specializations use Lua's luaL_check* functions to retrieve
the values from the stack. This means that no exceptions will be thrown - instead the error handling
is delegated to the Lua VM. Have a look at the
error handling documentation for more information.
In order to convenient register values in the global namespace, Luwra provides setGlobal and getGlobal.
// Register in the global namespace
luwra::setGlobal(lua, "almostPi", 3.14);
// Retrieve from globals
double almostPi = luwra::getGlobal<double>(lua, "almostPi");