Values
A fundamental part of Luwra is the Value template which acts as a type class. It is
used to define read
and push
behavior for certain types.
A type T
is considered readable if a function T Value<T>::read(State*, int)
exists. The
return type of that function need not be T
, instead it can be anything that is convertible to T
.
A type T
is considered pushable if a funtion void Value<T>::push(State*, T)
exists. In order
to avoid unnecessary copying, the second parameter type may also be const T&
.
Default Implementations
Several useful specializations 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::vector<T> | yes | no | table |
std::list<T> | yes | no | table |
std::map<K, V> | yes | yes | table |
lua_CFunction | yes | no | function |
Function | yes | yes | function, table or userdata |
Table | yes | yes | table |
Note: Some numeric types have a different size than their matching Lua type - they will be
truncated during read
or push
operations.
Arbitrary and User Types
Value provides a catch-all generalization for types that do not have a specialization of Value. Although these types are not known to Luwra, they are pushable and readable.
Instances of these so-called user types are constructed on the Lua stack as a full userdata. Additionally, a metatable that is specific to the given user type is attached to the userdata. This metatable allows us to check whether a userdata is an instance of a specific user type.
push
operations always copy or move instances of the user type onto the stack, whereas read
operations always reference the user type value on the stack.
By default, the metatables that are attached to the user type values are empty. Because of this, they provide no functionality to Lua and are never destructed (underlying storage is just freed). You can change this behavior, read more in the User Types section.
Extending Value
You can customize the read
and push
behavior for your own type T
. Simply modify the following
snippet and insert it outside of any namespace.
namespace luwra {
template <>
struct Value<T> {
static inline
T read(State* state, int index) {
return /* Return the instance of T that you have read at the given index */;
}
static inline
void push(State* state, const T& value) {
// Push the given value on top of the stack
}
};
}
Return Values
The template ReturnValues extends the push
functionality on top of
Value by allowing more complex types to be pushed onto the stack.
ReturnValues makes it possible to use std::tuple<...>
or std::pair<...>
as
return type of user-provided functions in order to mimic the ability of Lua functions to return
multiple values at once.
Read and Type Errors
Luwra does not handle errors. Instead it delegates the error handling to Lua. See Error Handling in C for more information.
It is highly recommended that you use a version of Lua that has been compiled as C++. Doing otherwise might lead to improper stack unwinding in case of an error which causes resources to be leaked, since the calling of destructors is not guaranteed.