[DEV] import basic c++ lua wrapper

This commit is contained in:
Edouard DUPIN 2014-12-16 22:21:37 +01:00
commit 11bbc0e7a1
26 changed files with 2299 additions and 0 deletions

98
README.md Normal file
View File

@ -0,0 +1,98 @@
LuaWrapper
Copyright (c) 2010-2013 Alexander Ames
Alexander.Ames@gmail.com
Original source :
https://bitbucket.org/alexames/luawrapper/src/fd9c4fdbf4b25034e3b8475a2c8da66b7caab427?at=default
LuaWrapper is a library designed to help bridge the gab between Lua and C++.
It is designed to be small (a single header file), simple, fast, and typesafe.
It has no external dependencies other than the lua library itself, and does not
need to be precompiled; the header can simply be dropped into a project and
used immediately. It even supports class inheritance to a certain degree.
Objects can be created in either Lua or C++, and passed back and forth.
Instructions on how to use the library follow, but a simple example project has
been set up to see basic library usage and can be found at
https://bitbucket.org/alexames/luawrapperexample
In Lua, the objects are userdata, but through tricky use of metatables, they
can be treated almost identically to tables. Each userdata has a __index and
__newindex function that maps to a table hidden storage table in the registry.
The main functions of interest are the following:
luaW_is<T>
luaW_to<T>
luaW_check<T>
luaW_push<T>
luaW_register<T>
luaW_extend<T, U>
luaW_hold<T>
luaW_release<T>
The first four functions allow you to manipulate arbitrary classes just like
you would the primitive types (e.g. numbers or strings). luaW_is<T> allows you
to check if the object is what you think it is, and will take the is-a
relationship into account unless the optional strict argument is set.
luaW_to<T> will return a pointer of type T to your object (unless it is of the
wrong type, in which case it will return NULL).
Registering a class:
Run luaW_register to create a table and metatable for your class. This creates
a table with the name you specify filled with the function from the table
argument in addition to the function new. This is generally for things you
think of as static methods in C++. The metatable becomes a metatable for each
object if your class. These can be thought of as member functions or methods.
You may also supply constructors and destructors for classes that do not have a
default constructor or that require special set up or tear down. You may
specify NULL as the constructor, which means that you will not be able to call
the new function on your class table. You will need to manually push objects
from C++. By default, the default constructor is used to create objects and a
simple call to delete is used to destroy them. When using the constructor, you
do not have access to the userdata's storage table. If you want to add or
adjust values to the storage table you can use the special post constructor
metamethod ("__postctor" or LUAW_POSTCTOR_KEY).
By default, LuaWrapper uses the address of C++ object to identify unique
objects. In some cases this is not desired, such as in the case of shared_ptrs.
Two shared_ptrs may themselves have unique locations in memory but still
represent the same object. For cases like that, you may specify an identifier
function which is responsible for pushing a key representing your object on to
the stack.
Extending a class:
To extend a class use the function luaW_extend<T, U>, where T is a class that
extends U. All functions in the base class will be available to the derived
class (except when they share a function name, in which case the derived
class's function wins).
Pointer Ownership:
Objects created from within Lua scripts (or that are created through luaW_new)
are considered to be owned by Lua. Ownership merely means that when all
references to it are removed, the garbage collector will delete the object. An
object created in C++ and pushed to Lua is not considered owned unless
luaW_hold is run on it. LuaWrapper uses a weak table to cache objects in order
to prevent the __gc metamethod from deleting pointers to objects that are still
in use. If an object is created in Lua and you do not want it to be owned by
Lua, you may call luaW_release on it.
Lua Wrapper Utilities:
A second file, called LuaWrapperUtil.hpp includes a number of additional
functions that may be useful, which build upon the core LuaWrapper API. The
main features are some functions which automatically cast interger types to
enum types and templated getters and setters for primitives an pointers to
objects. All functions in LuaWrapperUtil.hpp are prefixed with luaU_.
Documentation and some examples are provided in the comments of the file.
Contributions:
Thanks to Christopher Eykamp for the idea to use ephemeron tables to cache
userdata to avoid unnecessary allocations as wells as various other feedback.
Thanks to qwer1304 for the addition of enum support in the utilities functions.

19
license.txt Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2010-2011 Alexander Ames
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

643
luaWrapper/luaWrapper.hpp Normal file
View File

@ -0,0 +1,643 @@
/**
* Copyright (c) 2010-2013 Alexander Ames
* Alexander.Ames@gmail.com
* See Copyright Notice at the end of this file
*/
/** API Summary:
*
* LuaWrapper is a library designed to help bridge the gab between Lua and
* C++. It is designed to be small (a single header file), simple, fast,
* and typesafe. It has no external dependencies, and does not need to be
* precompiled; the header can simply be dropped into a project and used
* immediately. It even supports class inheritance to a certain degree. Objects
* can be created in either Lua or C++, and passed back and forth.
*
* The main functions of interest are the following:
* luaW_is<T>
* luaW_to<T>
* luaW_check<T>
* luaW_push<T>
* luaW_register<T>
* luaW_setfuncs<T>
* luaW_extend<T, U>
* luaW_hold<T>
* luaW_release<T>
*
* These functions allow you to manipulate arbitrary classes just like you
* would the primitive types (e.g. numbers or strings). If you are familiar
* with the normal Lua API the behavior of these functions should be very
* intuative.
*
* For more information see the README and the comments below
*/
#ifndef __LUA_WRAPPER_H__
#define __LUA_WRAPPER_H__
// If you are linking against Lua compiled in C++, define LUAW_NO_EXTERN_C
#include <lua/lua.h>
#include <lua/lauxlib.h>
#define LUAW_POSTCTOR_KEY "__postctor"
#define LUAW_EXTENDS_KEY "__extends"
#define LUAW_STORAGE_KEY "storage"
#define LUAW_CACHE_KEY "cache"
#define LUAW_CACHE_METATABLE_KEY "cachemetatable"
#define LUAW_HOLDS_KEY "holds"
#define LUAW_WRAPPER_KEY "LuaWrapper"
/**
* A simple utility function to adjust a given index
* Useful for when a parameter index needs to be adjusted
* after pushing or popping things off the stack
*/
inline int luaW_correctindex(lua_State* _L, int _index, int _correction) {
return _index < 0 ? _index - _correction : _index;
}
/**
* These are the default allocator and deallocator. If you would prefer an
* alternative option, you may select a different function when registering
* your class.
*/
template <typename T> T* luaW_defaultallocator(lua_State*) {
return new T();
}
template <typename T> void luaW_defaultdeallocator(lua_State*, T* _obj) {
delete _obj;
}
/**
* The identifier function is responsible for pushing a value unique to each
* object on to the stack. Most of the time, this can simply be the address
* of the pointer, but sometimes that is not adaquate. For example, if you
* are using shared_ptr you would need to push the address of the object the
* shared_ptr represents, rather than the address of the shared_ptr itself.
*/
template <typename T> void luaW_defaultidentifier(lua_State* _L, T* _obj) {
lua_pushlightuserdata(_L, _obj);
}
/**
* This class is what is used by LuaWrapper to contain the userdata. data
* stores a pointer to the object itself, and cast is used to cast toward the
* base class if there is one and it is necessary. Rather than use RTTI and
* typid to compare types, I use the clever trick of using the cast to compare
* types. Because there is at most one cast per type, I can use it to identify
* when and object is the type I want. This is only used internally.
*/
struct luaW_Userdata {
luaW_Userdata(void* vptr = NULL, luaW_Userdata (*udcast)(const luaW_Userdata&) = NULL) :
data(vptr),
cast(udcast) {
// nothing to do ...
}
void* data;
luaW_Userdata (*cast)(const luaW_Userdata&);
};
/**
* This class cannot actually to be instantiated. It is used only hold the
* table name and other information.
*/
template <typename T> class LuaWrapper {
public:
static const char* classname;
static void (*identifier)(lua_State*, T*);
static T* (*allocator)(lua_State*);
static void (*deallocator)(lua_State*, T*);
static luaW_Userdata (*cast)(const luaW_Userdata&);
static void (*postconstructorrecurse)(lua_State* _L, int numargs);
private:
LuaWrapper();
};
template <typename T> const char* LuaWrapper<T>::classname;
template <typename T> void (*LuaWrapper<T>::identifier)(lua_State*, T*);
template <typename T> T* (*LuaWrapper<T>::allocator)(lua_State*);
template <typename T> void (*LuaWrapper<T>::deallocator)(lua_State*, T*);
template <typename T> luaW_Userdata (*LuaWrapper<T>::cast)(const luaW_Userdata&);
template <typename T> void (*LuaWrapper<T>::postconstructorrecurse)(lua_State* _L, int _numargs);
/**
* Cast from an object of type T to an object of type U. This template
* function is instantiated by calling luaW_extend<T, U>(L). This is only used
* internally.
*/
template <typename T, typename U> luaW_Userdata luaW_cast(const luaW_Userdata& _obj) {
return luaW_Userdata(static_cast<U*>(static_cast<T*>(_obj.data)), LuaWrapper<U>::cast);
}
template <typename T, typename U> void luaW_identify(lua_State* _L, T* _obj) {
LuaWrapper<U>::identifier(_L, static_cast<U*>(_obj));
}
template <typename T> inline void luaW_wrapperfield(lua_State* _L, const char* _field) {
lua_getfield(_L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(_L, -1, _field); // ... LuaWrapper LuaWrapper.field
lua_getfield(_L, -1, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.field LuaWrapper.field.class
lua_replace(_L, -3); // ... LuaWrapper.field.class LuaWrapper.field
lua_pop(_L, 1); // ... LuaWrapper.field.class
}
/**
* Analogous to lua_is(boolean|string|*)
*
* Returns 1 if the value at the given acceptable index is of type T (or if
* strict is false, convertable to type T) and 0 otherwise.
*/
template <typename T> bool luaW_is(lua_State *_L, int _index, bool _strict = false) {
bool equal = false;// lua_isnil(_L, index);
if (!equal && lua_isuserdata(_L, _index) && lua_getmetatable(_L, _index)) {
// ... ud ... udmt
luaL_getmetatable(_L, LuaWrapper<T>::classname); // ... ud ... udmt Tmt
equal = lua_rawequal(_L, -1, -2) != 0;
if (!equal && !_strict) {
lua_getfield(_L, -2, LUAW_EXTENDS_KEY); // ... ud ... udmt Tmt udmt.extends
for (lua_pushnil(_L); lua_next(_L, -2); lua_pop(_L, 1)) {
// ... ud ... udmt Tmt udmt.extends k v
equal = lua_rawequal(_L, -1, -4) != 0;
if (equal) {
lua_pop(_L, 2); // ... ud ... udmt Tmt udmt.extends
break;
}
}
lua_pop(_L, 1); // ... ud ... udmt Tmt
}
lua_pop(_L, 2); // ... ud ...
}
return equal;
}
/**
* Analogous to lua_to(boolean|string|*)
*
* Converts the given acceptable index to a T*. That value must be of (or
* convertable to) type T; otherwise, returns NULL.
*/
template <typename T> T* luaW_to(lua_State* _L, int _index, bool _strict = false) {
if (luaW_is<T>(_L, _index, _strict)) {
luaW_Userdata* pud = static_cast<luaW_Userdata*>(lua_touserdata(_L, _index));
luaW_Userdata ud;
while ( !_strict
&& LuaWrapper<T>::cast != pud->cast) {
ud = pud->cast(*pud);
pud = &ud;
}
return static_cast<T*>(pud->data);
}
return NULL;
}
/**
* Analogous to luaL_check(boolean|string|*)
*
* Converts the given acceptable index to a T*. That value must be of (or
* convertable to) type T; otherwise, an error is raised.
*/
template <typename T> T* luaW_check(lua_State* _L, int _index, bool _strict = false) {
T* obj = NULL;
if (luaW_is<T>(_L, _index, _strict)) {
luaW_Userdata* pud = static_cast<luaW_Userdata*>(lua_touserdata(_L, _index));
luaW_Userdata ud;
while (!_strict && LuaWrapper<T>::cast != pud->cast) {
ud = pud->cast(*pud);
pud = &ud;
}
obj = (T*)pud->data;
} else {
const char *msg = lua_pushfstring(_L, "%s expected, got %s", LuaWrapper<T>::classname, luaL_typename(_L, _index));
luaL_argerror(_L, _index, msg);
}
return obj;
}
template <typename T> T* luaW_opt(lua_State* _L, int _index, T* _fallback = NULL, bool _strict = false) {
if (lua_isnil(_L, _index)) {
return _fallback;
} else {
return luaW_check<T>(_L, _index, _strict);
}
}
/**
* Analogous to lua_push(boolean|string|*)
*
* Pushes a userdata of type T onto the stack. If this object already exists in
* the Lua environment, it will assign the existing storage table to it.
* Otherwise, a new storage table will be created for it.
*/
template <typename T> void luaW_push(lua_State* _L, T* _obj) {
if (_obj) {
LuaWrapper<T>::identifier(_L, _obj); // ... id
luaW_wrapperfield<T>(_L, LUAW_CACHE_KEY); // ... id cache
lua_pushvalue(_L, -2); // ... id cache id
lua_gettable(_L, -2); // ... id cache obj
if (lua_isnil(_L, -1)) {
// Create the new luaW_userdata and place it in the cache
lua_pop(_L, 1); // ... id cache
lua_insert(_L, -2); // ... cache id
luaW_Userdata* ud = static_cast<luaW_Userdata*>(lua_newuserdata(_L, sizeof(luaW_Userdata))); // ... cache id obj
ud->data = _obj;
ud->cast = LuaWrapper<T>::cast;
lua_pushvalue(_L, -1); // ... cache id obj obj
lua_insert(_L, -4); // ... obj cache id obj
lua_settable(_L, -3); // ... obj cache
luaL_getmetatable(_L, LuaWrapper<T>::classname); // ... obj cache mt
lua_setmetatable(_L, -3); // ... obj cache
lua_pop(_L, 1); // ... obj
} else {
lua_replace(_L, -3); // ... obj cache
lua_pop(_L, 1); // ... obj
}
} else {
lua_pushnil(_L);
}
}
/**
* Instructs LuaWrapper that it owns the userdata, and can manage its memory.
* When all references to the object are removed, Lua is free to garbage
* collect it and delete the object.
*
* Returns true if luaW_hold took hold of the object, and false if it was
* already held
*/
template <typename T> bool luaW_hold(lua_State* _L, T* _obj) {
luaW_wrapperfield<T>(_L, LUAW_HOLDS_KEY); // ... holds
LuaWrapper<T>::identifier(_L, _obj); // ... holds id
lua_pushvalue(_L, -1); // ... holds id id
lua_gettable(_L, -3); // ... holds id hold
// If it's not held, hold it
if (!lua_toboolean(_L, -1)) {
// Apply hold boolean
lua_pop(_L, 1); // ... holds id
lua_pushboolean(_L, true); // ... holds id true
lua_settable(_L, -3); // ... holds
lua_pop(_L, 1); // ...
return true;
}
lua_pop(_L, 3); // ...
return false;
}
/**
* Releases LuaWrapper's hold on an object. This allows the user to remove
* all references to an object in Lua and ensure that Lua will not attempt to
* garbage collect it.
*
* This function takes the index of the identifier for an object rather than
* the object itself. This is because needs to be able to run after the object
* has already been deallocated. A wrapper is provided for when it is more
* convenient to pass in the object directly.
*/
template <typename T> void luaW_release(lua_State* _L, int _index) {
luaW_wrapperfield<T>(_L, LUAW_HOLDS_KEY); // ... id ... holds
lua_pushvalue(_L, luaW_correctindex(_L, _index, 1)); // ... id ... holds id
lua_pushnil(_L); // ... id ... holds id nil
lua_settable(_L, -3); // ... id ... holds
lua_pop(_L, 1); // ... id ...
}
template <typename T> void luaW_release(lua_State* _L, T* _obj) {
LuaWrapper<T>::identifier(_L, _obj); // ... id
luaW_release<T>(_L, -1); // ... id
lua_pop(_L, 1); // ...
}
template <typename T> void luaW_postconstructorinternal(lua_State* _L, int _numargs) {
// ... ud args...
if (LuaWrapper<T>::postconstructorrecurse) {
LuaWrapper<T>::postconstructorrecurse(_L, _numargs);
}
luaL_getmetatable(_L, LuaWrapper<T>::classname); // ... ud args... mt
lua_getfield(_L, -1, LUAW_POSTCTOR_KEY); // ... ud args... mt postctor
if (lua_type(_L, -1) == LUA_TFUNCTION) {
for (int i = 0; i < _numargs + 1; i++) {
lua_pushvalue(_L, -3 - _numargs); // ... ud args... mt postctor ud args...
}
lua_call(_L, _numargs + 1, 0); // ... ud args... mt
lua_pop(_L, 1); // ... ud args...
} else {
lua_pop(_L, 2); // ... ud args...
}
}
/**
* This function is called from Lua, not C++
*
* Calls the lua post-constructor (LUAW_POSTCTOR_KEY or "__postctor") on a
* userdata. Assumes the userdata is on the stack and numargs arguments follow
* it. This runs the LUAW_POSTCTOR_KEY function on T's metatable, using the
* object as the first argument and whatever else is below it as the rest of the
* arguments This exists to allow types to adjust values in thier storage table,
* which can not be created until after the constructor is called.
*/
template <typename T> void luaW_postconstructor(lua_State* _L, int _numargs) {
// ... ud args...
luaW_postconstructorinternal<T>(_L, _numargs); // ... ud args...
lua_pop(_L, _numargs); // ... ud
}
/**
* This function is generally called from Lua, not C++
*
* Creates an object of type T using the constructor and subsequently calls the
* post-constructor on it.
*/
template <typename T> inline int luaW_new(lua_State* _L, int _numargs) {
// ... args...
T* obj = LuaWrapper<T>::allocator(_L);
luaW_push<T>(_L, obj); // ... args... ud
luaW_hold<T>(_L, obj);
lua_insert(_L, -1 - _numargs); // ... ud args...
luaW_postconstructor<T>(_L, _numargs); // ... ud
return 1;
}
template <typename T> int luaW_new(lua_State* _L) {
return luaW_new<T>(_L, lua_gettop(_L));
}
/**
* This function is called from Lua, not C++
*
* The default metamethod to call when indexing into lua userdata representing
* an object of type T. This will first check the userdata's environment table
* and if it's not found there it will check the metatable. This is done so
* individual userdata can be treated as a table, and can hold thier own
* values.
*/
template <typename T> int luaW_index(lua_State* _L) {
// obj key
T* obj = luaW_to<T>(_L, 1);
luaW_wrapperfield<T>(_L, LUAW_STORAGE_KEY); // obj key storage
LuaWrapper<T>::identifier(_L, obj); // obj key storage id
lua_gettable(_L, -2); // obj key storage store
// Check if storage table exists
if (!lua_isnil(_L, -1)) {
lua_pushvalue(_L, -3); // obj key storage store key
lua_gettable(_L, -2); // obj key storage store store[k]
}
// If either there is no storage table or the key wasn't found
// then fall back to the metatable
if (lua_isnil(_L, -1)) {
lua_settop(_L, 2); // obj key
lua_getmetatable(_L, -2); // obj key mt
lua_pushvalue(_L, -2); // obj key mt k
lua_gettable(_L, -2); // obj key mt mt[k]
}
return 1;
}
/**
* This function is called from Lua, not C++
*
* The default metamethod to call when creating a new index on lua userdata
* representing an object of type T. This will index into the the userdata's
* environment table that it keeps for personal storage. This is done so
* individual userdata can be treated as a table, and can hold thier own
* values.
*/
template <typename T> int luaW_newindex(lua_State* _L) {
// obj key value
T* obj = luaW_check<T>(_L, 1);
luaW_wrapperfield<T>(_L, LUAW_STORAGE_KEY); // obj key value storage
LuaWrapper<T>::identifier(_L, obj); // obj key value storage id
lua_pushvalue(_L, -1); // obj key value storage id id
lua_gettable(_L, -3); // obj key value storage id store
// Add the storage table if there isn't one already
if (lua_isnil(_L, -1)) {
lua_pop(_L, 1); // obj key value storage id
lua_newtable(_L); // obj key value storage id store
lua_pushvalue(_L, -1); // obj key value storage id store store
lua_insert(_L, -3); // obj key value storage store id store
lua_settable(_L, -4); // obj key value storage store
}
lua_pushvalue(_L, 2); // obj key value ... store key
lua_pushvalue(_L, 3); // obj key value ... store key value
lua_settable(_L, -3); // obj key value ... store
return 0;
}
/**
* This function is called from Lua, not C++
*
* The __gc metamethod handles cleaning up userdata. The userdata's reference
* count is decremented and if this is the final reference to the userdata its
* environment table is nil'd and pointer deleted with the destructor callback.
*/
template <typename T> int luaW_gc(lua_State* _L) {
// obj
T* obj = luaW_to<T>(_L, 1);
LuaWrapper<T>::identifier(_L, obj); // obj key value storage id
luaW_wrapperfield<T>(_L, LUAW_HOLDS_KEY); // obj id counts count holds
lua_pushvalue(_L, 2); // obj id counts count holds id
lua_gettable(_L, -2); // obj id counts count holds hold
if (lua_toboolean(_L, -1) && LuaWrapper<T>::deallocator) {
LuaWrapper<T>::deallocator(_L, obj);
}
luaW_wrapperfield<T>(_L, LUAW_STORAGE_KEY); // obj id counts count holds hold storage
lua_pushvalue(_L, 2); // obj id counts count holds hold storage id
lua_pushnil(_L); // obj id counts count holds hold storage id nil
lua_settable(_L, -3); // obj id counts count holds hold storage
luaW_release<T>(_L, 2);
return 0;
}
/**
* Thakes two tables and registers them with Lua to the table on the top of the
* stack.
*
* This function is only called from LuaWrapper internally.
*/
inline void luaW_registerfuncs(lua_State* _L, const luaL_Reg _defaulttable[], const luaL_Reg _table[]) {
// ... T
#if LUA_VERSION_NUM == 502
if (_defaulttable) {
luaL_setfuncs(_L, _defaulttable, 0); // ... T
}
if (_table) {
luaL_setfuncs(_L, _table, 0); // ... T
}
#else
if (_defaulttable) {
luaL_register(_L, NULL, _defaulttable); // ... T
}
if (_table) {
luaL_register(_L, NULL, _table); // ... T
}
#endif
}
/**
* Initializes the LuaWrapper tables used to track internal state.
*
* This function is only called from LuaWrapper internally.
*/
inline void luaW_initialize(lua_State* _L) {
// Ensure that the LuaWrapper table is set up
lua_getfield(_L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
if (lua_isnil(_L, -1)) {
lua_newtable(_L); // ... nil {}
lua_pushvalue(_L, -1); // ... nil {} {}
lua_setfield(_L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... nil LuaWrapper
// Create a storage table
lua_newtable(_L); // ... LuaWrapper nil {}
lua_setfield(_L, -2, LUAW_STORAGE_KEY); // ... nil LuaWrapper
// Create a holds table
lua_newtable(_L); // ... LuaWrapper {}
lua_setfield(_L, -2, LUAW_HOLDS_KEY); // ... nil LuaWrapper
// Create a cache table, with weak values so that the userdata will not
// be ref counted
lua_newtable(_L); // ... nil LuaWrapper {}
lua_setfield(_L, -2, LUAW_CACHE_KEY); // ... nil LuaWrapper
lua_newtable(_L); // ... nil LuaWrapper {}
lua_pushstring(_L, "v"); // ... nil LuaWrapper {} "v"
lua_setfield(_L, -2, "__mode"); // ... nil LuaWrapper {}
lua_setfield(_L, -2, LUAW_CACHE_METATABLE_KEY); // ... nil LuaWrapper
lua_pop(_L, 1); // ... nil
}
lua_pop(_L, 1); // ...
}
/**
* Run luaW_register or luaW_setfuncs to create a table and metatable for your
* class. These functions create a table with filled with the function from
* the table argument in addition to the functions new and build (This is
* generally for things you think of as static methods in C++). The given
* metatable argument becomes a metatable for each object of your class. These
* can be thought of as member functions or methods.
*
* You may also supply constructors and destructors for classes that do not
* have a default constructor or that require special set up or tear down. You
* may specify NULL as the constructor, which means that you will not be able
* to call the new function on your class table. You will need to manually push
* objects from C++. By default, the default constructor is used to create
* objects and a simple call to delete is used to destroy them.
*
* By default LuaWrapper uses the address of C++ object to identify unique
* objects. In some cases this is not desired, such as in the case of
* shared_ptrs. Two shared_ptrs may themselves have unique locations in memory
* but still represent the same object. For cases like that, you may specify an
* identifier function which is responsible for pushing a key representing your
* object on to the stack.
*
* luaW_register will set table as the new value of the global of the given
* name. luaW_setfuncs is identical to luaW_register, but it does not set the
* table globally. As with luaL_register and luaL_setfuncs, both funcstions
* leave the new table on the top of the stack.
*/
template <typename T> void luaW_setfuncs(lua_State* _L,
const char* _classname,
const luaL_Reg* _table,
const luaL_Reg* _metatable,
T* (*_allocator)(lua_State*) = luaW_defaultallocator<T>,
void (*_deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>,
void (*_identifier)(lua_State*, T*) = luaW_defaultidentifier<T>) {
luaW_initialize(_L);
LuaWrapper<T>::classname = _classname;
LuaWrapper<T>::identifier = _identifier;
LuaWrapper<T>::allocator = _allocator;
LuaWrapper<T>::deallocator = _deallocator;
const luaL_Reg defaulttable[] = {
{ "new", luaW_new<T> },
{ NULL, NULL }
};
const luaL_Reg defaultmetatable[] = {
{ "__index", luaW_index<T> },
{ "__newindex", luaW_newindex<T> },
{ "__gc", luaW_gc<T> },
{ NULL, NULL }
};
// Set up per-type tables
lua_getfield(_L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_newtable(_L); // ... LuaWrapper LuaWrapper.storage {}
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
lua_pop(_L, 1); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_newtable(_L); // ... LuaWrapper LuaWrapper.holds {}
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
lua_pop(_L, 1); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
lua_newtable(_L); // ... LuaWrapper LuaWrapper.cache {}
luaW_wrapperfield<T>(_L, LUAW_CACHE_METATABLE_KEY); // ... LuaWrapper LuaWrapper.cache {} cmt
lua_setmetatable(_L, -2); // ... LuaWrapper LuaWrapper.cache {}
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
lua_pop(_L, 2); // ...
// Open table
lua_newtable(_L); // ... T
luaW_registerfuncs(_L, _allocator ? defaulttable : NULL, _table); // ... T
// Open metatable, set up extends table
luaL_newmetatable(_L, _classname); // ... T mt
lua_newtable(_L); // ... T mt {}
lua_setfield(_L, -2, LUAW_EXTENDS_KEY); // ... T mt
luaW_registerfuncs(_L, defaultmetatable, _metatable); // ... T mt
lua_setfield(_L, -2, "metatable"); // ... T
}
template <typename T> void luaW_register(lua_State* _L,
const char* _classname,
const luaL_Reg* _table,
const luaL_Reg* _metatable,
T* (*_allocator)(lua_State*) = luaW_defaultallocator<T>,
void (*_deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>,
void (*_identifier)(lua_State*, T*) = luaW_defaultidentifier<T>) {
luaW_setfuncs(_L, _classname, _table, _metatable, _allocator, _deallocator, _identifier); // ... T
lua_pushvalue(_L, -1); // ... T T
lua_setglobal(_L, _classname); // ... T
}
/**
* luaW_extend is used to declare that class T inherits from class U. All
* functions in the base class will be available to the derived class (except
* when they share a function name, in which case the derived class's function
* wins). This also allows luaW_to<T> to cast your object apropriately, as
* casts straight through a void pointer do not work.
*/
template <typename T, typename U> void luaW_extend(lua_State* _L) {
if(!LuaWrapper<T>::classname) {
luaL_error(_L, "attempting to call extend on a type that has not been registered");
}
if(!LuaWrapper<U>::classname) {
luaL_error(_L, "attempting to extend %s by a type that has not been registered", LuaWrapper<T>::classname);
}
LuaWrapper<T>::cast = luaW_cast<T, U>;
LuaWrapper<T>::identifier = luaW_identify<T, U>;
LuaWrapper<T>::postconstructorrecurse = luaW_postconstructorinternal<U>;
luaL_getmetatable(_L, LuaWrapper<T>::classname); // mt
luaL_getmetatable(_L, LuaWrapper<U>::classname); // mt emt
// Point T's metatable __index at U's metatable for inheritance
lua_newtable(_L); // mt emt {}
lua_pushvalue(_L, -2); // mt emt {} emt
lua_setfield(_L, -2, "__index"); // mt emt {}
lua_setmetatable(_L, -3); // mt emt
// Set up per-type tables to point at parent type
lua_getfield(_L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_getfield(_L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.storage U
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
lua_pop(_L, 1); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_getfield(_L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.holds U
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
lua_pop(_L, 1); // ... LuaWrapper
lua_getfield(_L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
lua_getfield(_L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.cache U
lua_setfield(_L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
lua_pop(_L, 2); // ...
// Make a list of all types that inherit from U, for type checking
lua_getfield(_L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends
lua_pushvalue(_L, -2); // mt emt mt.extends emt
lua_setfield(_L, -2, LuaWrapper<U>::classname); // mt emt mt.extends
lua_getfield(_L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends emt.extends
for (lua_pushnil(_L); lua_next(_L, -2); lua_pop(_L, 1)) {
// mt emt mt.extends emt.extends k v
lua_pushvalue(_L, -2); // mt emt mt.extends emt.extends k v k
lua_pushvalue(_L, -2); // mt emt mt.extends emt.extends k v k
lua_rawset(_L, -6); // mt emt mt.extends emt.extends k v
}
lua_pop(_L, 4); // mt emt
}
#endif

View File

@ -0,0 +1,737 @@
/**
* Copyright (c) 2010-2013 Alexander Ames
* Alexander.Ames@gmail.com
* See Copyright Notice at the end of this file
*/
/** API Summary:
*
* This is a set of utility functions that add to the functionalit of
* LuaWrapper. Over time I found myself repeating a few patterns, such as
* writing many trvial getter and setter functions, and wanting pass ownership
* of one object to another and have the Lua script properly handle that case.
*
* This file contains the additional functions that I've added but that do
* not really belong in the core API.
*/
#ifndef __LUA__WRAPPER__UTILS_HPP__
#define __LUA__WRAPPER__UTILS_HPP__
#include <luaWrapper/luaWrapper.hpp>
#include <type_traits>
#ifndef LUAW_STD
#define LUAW_STD std
#endif
/**
* @brief This template removes reference and const qualifier from the type
*/
template <typename T> struct luaW_remove_cr {
typedef typename std::remove_const<typename std::remove_reference<T>::type>::type type;
};
/**
* A set of templated luaL_check and lua_push functions for use in the getters
* and setters below
*
* It is often useful to override luaU_check, luaU_to and/or luaU_push to
* operate on your own simple types rather than register your type with
* LuaWrapper, especially with small objects.
*/
template<typename U, typename = void> struct luaU_Impl {
static U luaU_check(lua_State* _L, int _index);
static U luaU_to(lua_State* _L, int _index);
static void luaU_push(lua_State* _L, const U& _value);
};
template<typename U> U luaU_check(lua_State* _L, int _index) {
return luaU_Impl<U>::luaU_check(_L, _index);
}
template<typename U> U luaU_to(lua_State* _L, int _index) {
return luaU_Impl<U>::luaU_to(_L, _index);
}
template<typename U> void luaU_push(lua_State* _L, const U& _value) {
luaU_Impl<U>::luaU_push(_L, _value);
}
/** This is slightly different than the previous three functions in that you
* shouldn't need to write your own version of it, since it uses luaU_check
* automatically.
*/
template<typename U> U luaU_opt(lua_State* _L, int _index, const U& _fallback = U()) {
if (lua_isnil(_L, _index))
return _fallback;
else
return luaU_Impl<U>::luaU_check(_L, _index);
}
template<> struct luaU_Impl<bool> {
static bool luaU_check(lua_State* _L, int _index) {
return lua_toboolean(_L, _index) != 0;
}
static bool luaU_to(lua_State* _L, int _index) {
return lua_toboolean(_L, _index) != 0;
}
static void luaU_push(lua_State* _L, const bool& _value) {
lua_pushboolean(_L, _value);
}
};
template<> struct luaU_Impl<const char*> {
static const char* luaU_check(lua_State* _L, int _index) {
return luaL_checkstring(_L, _index);
}
static const char* luaU_to(lua_State* _L, int _index) {
return lua_tostring(_L, _index);
}
static void luaU_push(lua_State* _L, const char* const& _value) {
lua_pushstring(_L, _value);
}
};
template<> struct luaU_Impl<unsigned int> {
static unsigned int luaU_check(lua_State* _L, int _index) {
return static_cast<unsigned int>(luaL_checkinteger(_L, _index));
}
static unsigned int luaU_to(lua_State* _L, int _index) {
return static_cast<unsigned int>(lua_tointeger(_L, _index));
}
static void luaU_push(lua_State* _L, const unsigned int& _value) {
lua_pushinteger(_L, _value);
}
};
template<> struct luaU_Impl<int> {
static int luaU_check(lua_State* _L, int _index) {
return static_cast<int>(luaL_checkinteger(_L, _index));
}
static int luaU_to(lua_State* _L, int _index) {
return static_cast<int>(lua_tointeger(_L, _index));
}
static void luaU_push(lua_State* _L, const int& _value) {
lua_pushinteger(_L, _value);
}
};
template<> struct luaU_Impl<unsigned char> {
static unsigned char luaU_check(lua_State* _L, int _index) {
return static_cast<unsigned char>(luaL_checkinteger(_L, _index));
}
static unsigned char luaU_to(lua_State* _L, int _index) {
return static_cast<unsigned char>(lua_tointeger(_L, _index));
}
static void luaU_push(lua_State* _L, const unsigned char& _value) {
lua_pushinteger(_L, _value);
}
};
template<> struct luaU_Impl<char> {
static char luaU_check(lua_State* _L, int _index) {
return static_cast<char>(luaL_checkinteger(_L, _index));
}
static char luaU_to(lua_State* _L, int _index) {
return static_cast<char>(lua_tointeger(_L, _index));
}
static void luaU_push(lua_State* _L, const char& _value) {
lua_pushinteger(_L, _value);
}
};
template<> struct luaU_Impl<float> {
static float luaU_check(lua_State* _L, int _index) {
return static_cast<float>(luaL_checknumber(_L, _index));
}
static float luaU_to(lua_State* _L, int _index) {
return static_cast<float>(lua_tonumber(_L, _index));
}
static void luaU_push(lua_State* _L, const float& _value) {
lua_pushnumber(_L, _value);
}
};
template<> struct luaU_Impl<double> {
static double luaU_check(lua_State* _L, int _index) {
return static_cast<double>(luaL_checknumber(_L, _index));
}
static double luaU_to(lua_State* _L, int _index) {
return static_cast<double>(lua_tonumber(_L, _index));
}
static void luaU_push(lua_State* _L, const double& _value) {
lua_pushnumber(_L, _value);
}
};
template<typename T> struct luaU_Impl<T, typename LUAW_STD::enable_if<LUAW_STD::is_enum<T>::value>::type> {
static T luaU_check(lua_State* _L, int _index) {
return static_cast<T>(luaL_checkinteger(_L, _index));
}
static T luaU_to(lua_State* _L, int _index) {
return static_cast<T>(lua_tointeger(_L, _index));
}
static void luaU_push(lua_State* _L, const T& _value) {
lua_pushnumber(_L, static_cast<int>(_value));
}
};
template<typename T> struct luaU_Impl<T*, typename LUAW_STD::enable_if<LUAW_STD::is_class<T>::value>::type> {
static T* luaU_check(lua_State* _L, int _index) {
return luaW_check<T>(_L, _index);
}
static T* luaU_to(lua_State* _L, int _index) {
return luaW_to<T>(_L, _index);
}
static void luaU_push(lua_State* _L, T*& _value) {
luaW_push <T>(_L, _value);
}
static void luaU_push(lua_State* _L, T* _value) {
luaW_push <T>(_L, _value);
}
};
/**
* These are just some functions I've always felt should exist
*/
template <typename U> inline U luaU_getfield(lua_State* _L, int _index, const char* _field) {
static_assert(!LUAW_STD::is_same<U, const char*>::value,
"luaU_getfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
lua_getfield(_L, _index, _field);
U val = luaU_to<U>(_L, -1);
lua_pop(_L, 1);
return val;
}
template <typename U> inline U luaU_checkfield(lua_State* _L, int _index, const char* _field) {
static_assert(!LUAW_STD::is_same<U, const char*>::value,
"luaU_checkfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
lua_getfield(_L, _index, _field);
U val = luaU_check<U>(_L, -1);
lua_pop(_L, 1);
return val;
}
template <typename U> inline U luaU_optfield(lua_State* _L, int _index, const char* _field, const U& _fallback = U()) {
static_assert(!LUAW_STD::is_same<U, const char*>::value,
"luaU_getfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
lua_getfield(_L, _index, _field);
U val = luaU_opt<U>(_L, -1, _fallback);
lua_pop(_L, 1);
return val;
}
template <typename U> inline void luaU_setfield(lua_State* _L, int _index, const char* _field, U _val) {
luaU_push<U>(_L, _val);
lua_setfield(_L, luaW_correctindex(_L, _index, 1), _field);
}
/** A set of trivial getter and setter templates. These templates are designed
* to call trivial getters or setters.
*
* There are four forms:
* 1. Getting or setting a public member variable that is of a primitive type
* 2. Getting or setting a public member variable that is a pointer to an
* object
* 3. Getting or setting a private member variable that is of a primitive type
* through a getter or setter
* 3. Getting or setting a private member variable that is is a pointer to an
* object through a getter or setter
*
* The interface to all of them is the same however. In addition to plain
* getter and setter functions, there is a getset which does both - if an
* argument is supplied it attempts to set the value, and in either case it
* returns the value. In your lua table declaration in C++ rather than write
* individiual wrappers for each getter and setter you may do the following:
*
* static luaL_reg Foo_metatable[] =
* {
* { "GetBar", luaU_get<Foo, bool, &Widget::GetBar> },
* { "SetBar", luaU_set<Foo, bool, &Widget::SetBar> },
* { "Bar", luaU_getset<Foo, bool, &Widget::GetBar, &Widget::SetBar> },
* { NULL, NULL }
* }
*
* Getters and setters must have one of the following signatures:
* void T::Setter(U _value);
* void T::Setter(U* _value);
* void T::Setter(const U& _value);
* U Getter() const;
* U* Getter() const;
*
* In this example, the variable Foo::bar is private and so it is accessed
* through getter and setter functions. If Foo::bar were public, it could be
* accessed directly, like so:
*
* static luaL_reg Foo_metatable[] =
* {
* { "GetBar", luaU_get<Foo, bool, &Widget::bar> },
* { "SetBar", luaU_set<Foo, bool, &Widget::bar> },
* { "Bar", luaU_getset<Foo, bool, &Widget::bar> },
* }
*
* In a Lua script, you can now use foo:GetBar(), foo:SetBar() and foo:Bar()
*/
template <typename T, typename U, U T::*Member> int luaU_get(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
luaU_push<U>(_L, obj->*Member);
return 1;
}
template <typename T, typename U, U* T::*Member> int luaU_get(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
luaW_push<U>(_L, obj->*Member);
return 1;
}
template <typename T, typename U, U (T::*Getter)() const> int luaU_get(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
luaU_push<U>(_L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, const U& (T::*Getter)() const> int luaU_get(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
luaU_push<U>(_L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, U* (T::*Getter)() const> int luaU_get(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
luaW_push<U>(_L, (obj->*Getter)());
return 1;
}
template <typename T, typename U, U T::*Member> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
obj->*Member = luaU_check<U>(_L, 2);
}
return 0;
}
template <typename T, typename U, U* T::*Member> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
U* member = luaW_opt<U>(_L, 2);
obj->*Member = member;
}
return 0;
}
template <typename T, typename U, const U* T::*Member> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
U* member = luaW_opt<U>(_L, 2);
obj->*Member = member;
}
return 0;
}
template <typename T, typename U, const U* T::*Member> int luaU_setandrelease(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
U* member = luaW_opt<U>(_L, 2);
obj->*Member = member;
if (member) {
luaW_release<U>(_L, member);
}
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(U)> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
(obj->*Setter)(luaU_check<U>(_L, 2));
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(const U&)> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
(obj->*Setter)(luaU_check<U>(_L, 2));
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(U*)> int luaU_set(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
U* member = luaW_opt<U>(_L, 2);
(obj->*Setter)(member);
}
return 0;
}
template <typename T, typename U, void (T::*Setter)(U*)> int luaU_setandrelease(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj) {
U* member = luaW_opt<U>(_L, 2);
(obj->*Setter)(member);
if (member) {
luaW_release<U>(_L, member);
}
}
return 0;
}
template <typename T, typename U, U T::*Member> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
obj->*Member = luaU_check<U>(_L, 2);
return 0;
} else {
luaU_push<U>(_L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U* T::*Member> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
U* member = luaW_opt<U>(_L, 2);
obj->*Member = member;
return 0;
} else {
luaW_push<U>(_L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U* T::*Member> int luaU_getsetandrelease(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
U* member = luaW_opt<U>(_L, 2);
obj->*Member = member;
if (member)
luaW_release<U>(_L, member);
return 0;
} else {
luaW_push<U>(_L, obj->*Member);
return 1;
}
}
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(U)> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
(obj->*Setter)(luaU_check<U>(_L, 2));
return 0;
} else {
luaU_push<U>(_L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(const U&)> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
(obj->*Setter)(luaU_check<U>(_L, 2));
return 0;
} else {
luaU_push<U>(_L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, const U& (T::*Getter)() const, void (T::*Setter)(const U&)> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
(obj->*Setter)(luaU_check<U>(_L, 2));
return 0;
} else {
luaU_push<U>(_L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)> int luaU_getset(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
U* member = luaW_opt<U>(_L, 2);
(obj->*Setter)(member);
return 0;
} else {
luaW_push<U>(_L, (obj->*Getter)());
return 1;
}
}
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)> int luaU_getsetandrelease(lua_State* _L) {
T* obj = luaW_check<T>(_L, 1);
if (obj && lua_gettop(_L) >= 2) {
U* member = luaW_opt<U>(_L, 2);
(obj->*Setter)(member);
if (member)
luaW_release<U>(_L, member);
return 0;
} else {
luaW_push<U>(_L, (obj->*Getter)());
return 1;
}
}
/**
* luaU_func is a special macro that expands into a simple function wrapper.
* Unlike the getter setters above, you merely need to name the function you
* would like to wrap.
*
* For example,
*
* struct Foo
* {
* int DoSomething(int, const char*);
* };
*
* static luaL_reg Foo_metatable[] =
* {
* { "DoSomething", luaU_func(&Foo::DoSomething) },
* { NULL, NULL }
* }
*
* void RegisterFoo(lua_State* L)
* {
* luaW_register<Foo>(L, "Foo", NULL, Foo_metatable);
* }
*
* This macro will expand based on the function signature of Foo::DoSomething
* In this example, it would expand into the following wrapper:
*
* luaU_push(luaW_check<T>(L, 1)->DoSomething(luaU_check<int>(L, 2), luaU_check<const char*>(L, 3)));
* return 1;
*
* In this example there is only one member function called DoSomething. In some
* cases there may be multiple overrides for a function. For those cases, an
* additional macro has been provided, luaU_funcsig, which takes the entire
* function signature. The arguments to the macro reflect the order they would
* appear in the function signature: return type, type name, function name, and
* finally a list of all the argument types. For example:
*
* struct Foo
* {
* int DoSomething (const char*);
* int DoSomething (const char*, int);
* };
*
* const struct luaL_Reg Foo_metatable[] =
* {
* {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*)) },
* {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*, int)) },
* { NULL, NULL }
* };
*
* There`s also support for static and freestanding functions. Macros luaU_staticfunc
* and luaU_staticfuncsig work equally to luaU_func and luaU_funcsig, except for that
* you need to provide a separate metatable for static functions.
* For example,
*
* struct Foo
* {
* int DoSomething(int, const char*);
* static void DoSomethingElse(int a, int b, float c);
* };
*
* static luaL_reg Foo_metatable[] =
* {
* { "DoSomething", luaU_func(&Foo::DoSomething) },
* { NULL, NULL }
* };
*
* static luaL_reg Foo_metatable_static[] =
* {
* { "DoSomethingElse", luaU_staticfunc(&Foo::DoSomethingElse) },
* { NULL, NULL }
* };
*
* void RegisterFoo(lua_State* L)
* {
* luaW_register<Foo>(L, "Foo", Foo_metatable_static, Foo_metatable);
* }
*
* After that you will be able to use Foo class from Lua like this:
* local foo = Foo.new()
* foo:DoSomething(42, 'The Ultimate Question of Life, the Universe, and Everything.') -- member function call
* Foo:DoSomethingElse(30, 12, 3.1459) -- Static function call
*
* These macros and it's underlying templates are somewhat experimental and some
* refinements are probably needed. There are cases where it does not
* currently work and I expect some changes can be made to refine its behavior.
*/
#define luaU_func(memberfunc) &luaU_MemberFuncWrapper<decltype(memberfunc),memberfunc>::call
#define luaU_funcsig(returntype, type, funcname, ...) luaU_func(static_cast<returntype (type::*)(__VA_ARGS__)>(&type::funcname))
#define luaU_staticfunc(func) &luaU_StaticFuncWrapper<decltype(func),func>::call
#define luaU_staticfuncsig(returntype, type, funcname, ...) luaU_staticfunc(static_cast<returntype (*)(__VA_ARGS__)>(&type::funcname))
template<int... ints> struct luaU_IntPack { };
template<int start, int count, int... tail> struct luaU_MakeIntRangeType {
typedef typename luaU_MakeIntRangeType<start, count-1, start+count-1, tail...>::type type;
};
template<int start, int... tail> struct luaU_MakeIntRangeType<start, 0, tail...> {
typedef luaU_IntPack<tail...> type;
};
template<int start, int count> inline typename luaU_MakeIntRangeType<start, count>::type luaU_makeIntRange() {
return typename luaU_MakeIntRangeType<start, count>::type();
}
/**
* Member function wrapper
*/
template<class MemFunPtrType, MemFunPtrType MemberFunc> struct luaU_MemberFuncWrapper;
template<class T, class ReturnType, class... Args, ReturnType(T::*MemberFunc)(Args...)> struct luaU_MemberFuncWrapper<ReturnType (T::*)(Args...), MemberFunc> {
public:
static int call(lua_State* _L) {
return callImpl(_L, luaU_makeIntRange<2,sizeof...(Args)>());
}
private:
template<int... indices> static int callImpl(lua_State* _L, luaU_IntPack<indices...>) {
luaU_push<ReturnType>(_L, (luaW_check<T>(_L, 1)->*MemberFunc)(luaU_check<typename luaW_remove_cr<Args>::type>(_L, indices)...));
return 1;
}
};
template<class T, class... Args, void(T::*MemberFunc)(Args...)> struct luaU_MemberFuncWrapper<void(T::*)(Args...), MemberFunc> {
public:
static int call(lua_State* _L) {
return callImpl(_L, luaU_makeIntRange<2, sizeof...(Args)>());
}
private:
template<int... indices> static int callImpl(lua_State* _L, luaU_IntPack<indices...>) {
(luaW_check<T>(_L, 1)->*MemberFunc)(luaU_check<typename luaW_remove_cr<Args>::type>(_L, indices)...);
return 0;
}
};
/**
* static function wrapper
*/
template<class FunPtrType, FunPtrType Func> struct luaU_StaticFuncWrapper;
template<class ReturnType, class... Args, ReturnType(*Func)(Args...)> struct luaU_StaticFuncWrapper<ReturnType(*)(Args...), Func> {
public:
static int call(lua_State* _L) {
return callImpl(_L, luaU_makeIntRange<2,sizeof...(Args)>());
}
private:
template<int... indices> static int callImpl(lua_State* _L, luaU_IntPack<indices...>) {
luaU_push<ReturnType>(_L, (*Func)(luaU_check<typename luaW_remove_cr<Args>::type>(_L, indices)...));
return 1;
}
};
template<class... Args, void(*Func)(Args...)> struct luaU_StaticFuncWrapper<void(*)(Args...), Func> {
public:
static int call(lua_State* _L) {
return callImpl(_L, luaU_makeIntRange<2, sizeof...(Args)>());
}
private:
template<int... indices>
static int callImpl(lua_State* _L, luaU_IntPack<indices...>) {
(*Func)(luaU_check<typename luaW_remove_cr<Args>::type>(_L, indices)...);
return 0;
}
};
/**
* Calls the copy constructor for an object of type T.
* Arguments may be passed in, in case they're needed for the postconstructor
*
* e.g.
*
* C++:
* luaL_reg Foo_Metatable[] =
* {
* { "clone", luaU_clone<Foo> },
* { NULL, NULL }
* };
*
* Lua:
* foo = Foo.new()
* foo2 = foo:clone()
*/
template <typename T> int luaU_clone(lua_State* _L) {
T* obj = new T(*luaW_check<T>(_L, 1));
lua_remove(_L, 1); // ...
int numargs = lua_gettop(_L);
luaW_push<T>(_L, obj); // ... clone
luaW_hold<T>(_L, obj);
luaW_postconstructor<T>(_L, numargs);
return 1;
}
/**
* luaU_build is intended to be used to initialize many values by passing in a
* table. They keys of the table are used as function names, and values are
* used as arguments to the function. This is intended to be used on functions
* that are simple setters.
*
* For example, if luaU_build is set as the post constructor, you can
* initialize an object as so:
*
* f = Foo.new
* {
* X = 10;
* Y = 20;
* }
*
* After the object is constructed, luaU_build will do the equivalent of
* calling f:X(10) and f:Y(20).
*/
template <typename T> int luaU_build(lua_State* _L) {
// obj {}
lua_insert(_L, -2); // {} obj
if (lua_type(_L, 1) == LUA_TTABLE) {
for (lua_pushnil(_L); lua_next(_L, 1); lua_pop(_L, 1)) {
// {} obj k v
lua_pushvalue(_L, -2); // {} obj k v k
lua_gettable(_L, -4); // {} obj k v ud[k]
lua_pushvalue(_L, -4); // {} obj k v ud[k] ud
lua_pushvalue(_L, -3); // {} obj k v ud[k] ud v
lua_call(_L, 2, 0); // {} obj k v
}
// {} ud
}
return 0;
}
/**
* Takes the object of type T at the top of the stack and stores it in on a
* table with the name storagetable, on the table at the specified _index.
*
* You may manually call luaW_hold and luaW_release to handle pointer
* ownership, but it is often easier to simply store a Lua userdata on a table
* that is owned by its parent. This ensures that your object will not be
* prematurely freed, and that it can only be destoryed after its parent.
*
* e.g.
*
* Foo* parent = luaW_check<Foo>(_L, 1);
* Bar* child = luaW_check<Bar>(_L, 2);
* if (parent && child)
* {
* luaU_store<Bar>(_L, 1, "Children");
* parent->AddChild(child);
* }
*/
template <typename T> void luaU_store(lua_State* _L, int _index, const char* _storagetable, const char* _key = NULL) {
// ... store ... obj
lua_getfield(_L, _index, _storagetable); // ... store ... obj store.storagetable
if (_key) {
lua_pushstring(_L, _key); // ... store ... obj store.storagetable key
} else {
LuaWrapper<T>::identifier(_L, luaW_to<T>(_L, -2)); // ... store ... obj store.storagetable key
}
lua_pushvalue(_L, -3); // ... store ... obj store.storagetable key obj
lua_settable(_L, -3); // ... store ... obj store.storagetable
lua_pop(_L, 1); // ... store ... obj
}
#endif

21
lutin_luaWrapper.py Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
def get_desc():
return "luwWrapper : simple Lua automatic wrapper"
def create(target):
# module name is 'edn' and type binary.
myModule = module.Module(__file__, 'luaWrapper', 'LIBRARY')
# name of the dependency
myModule.add_module_depend('lua')
myModule.add_export_path(tools.get_current_path(__file__))
# add the currrent module at the
return myModule

15
sample/README Normal file
View File

@ -0,0 +1,15 @@
LuaWrapperExample
Copyright (c) 2010-2013 Alexander Ames
Alexander.Ames@gmail.com
This repository is a very simple example of how you can use LuaWrapper to
create typesafe bindings between Lua and C++. This is not a complete tutorial,
but rather just a quick project to illustrate how the library is used.
Specifically, it does not cover the hold, release or clean functions.
See the README functions in ./example1/ and ./example2/ for more information.
The main repository for LuaWrapper can be found at
https://bitbucket.org/alexames/luawrapper/

View File

@ -0,0 +1,32 @@
#include "BankAccount.hpp"
float BankAccount::s_totalMoneyInBank = 0;
BankAccount::BankAccount(const char* _owner, float _balance) :
m_owner(_owner) ,
m_balance(_balance) {
s_totalMoneyInBank += _balance;
}
const char* BankAccount::getOwnerName() const {
return m_owner;
}
void BankAccount::deposit(float _amount) {
s_totalMoneyInBank += _amount;
m_balance += _amount;
}
void BankAccount::withdraw(float _amount) {
s_totalMoneyInBank -= _amount;
m_balance -= _amount;
}
float BankAccount::checkBalance() const {
return m_balance;
}
float BankAccount::checkTotalMoneyInBank() {
return s_totalMoneyInBank;
}

View File

@ -0,0 +1,21 @@
#ifndef EXAMPLE_HPP_
#define EXAMPLE_HPP_
#include <string>
#include <iostream>
class BankAccount {
public:
BankAccount(const char* _owner, float _balance);
const char* getOwnerName() const;
void deposit(float _amount);
void withdraw(float _amount);
float checkBalance() const;
static float checkTotalMoneyInBank();
private:
const char* m_owner;
float m_balance;
static float s_totalMoneyInBank;
};
#endif // EXAMPLE_HPP_

View File

@ -0,0 +1,93 @@
#include <iostream>
#include <string>
#include <lua/lua.h>
#include <lua/lauxlib.h>
#include <luaWrapper/luaWrapper.hpp>
#include "BankAccount.hpp"
using namespace std;
/**
* Allocator
*
* Types that do not have a default contructor require you to write an allocator function.
* This function is passed to luaW_register.
*/
BankAccount* BankAccount_new(lua_State *_L) {
const char* owner = luaL_checkstring(_L, 1);
float balance = luaL_checknumber(_L, 2);
return new BankAccount(owner, balance);
}
/**
* Static Functions
*
* These functions can be called directly from the BankAccount table in lua
*/
int BankAccount_checkTotalMoneyInBank(lua_State *_L) {
lua_pushnumber(_L, BankAccount::checkTotalMoneyInBank());
return 1;
}
/**
* Member Functions
*
* These functions are stored on the BankAccount.metatable table.
* All BankAccount objects in Lua have access to the functions defined there
* by the use of special a __index metatmethod that is set up by LuaWrapper.
*/
int BankAccount_getOwnerName(lua_State *_L) {
BankAccount* account = luaW_check<BankAccount>(_L, 1);
lua_pushstring(_L, account->getOwnerName());
return 1;
}
int BankAccount_deposit(lua_State* _L) {
BankAccount* account = luaW_check<BankAccount>(_L, 1);
float amount = luaL_checknumber(_L, 2);
account->deposit(amount);
return 0;
}
int BankAccount_withdraw(lua_State* _L) {
BankAccount* account = luaW_check<BankAccount>(_L, 1);
float amount = luaL_checknumber(_L, 2);
account->withdraw(amount);
return 0;
}
int BankAccount_checkBalance(lua_State* _L) {
BankAccount* account = luaW_check<BankAccount>(_L, 1);
lua_pushnumber(_L, account->checkBalance());
return 1;
}
static luaL_Reg BankAccount_table[] = {
{ "checkTotalMoneyInBank", BankAccount_checkTotalMoneyInBank },
{ NULL, NULL }
};
static luaL_Reg BankAccount_metatable[] = {
{ "getOwnerName", BankAccount_getOwnerName },
{ "deposit", BankAccount_deposit },
{ "withdraw", BankAccount_withdraw },
{ "checkBalance", BankAccount_checkBalance },
{ NULL, NULL }
};
int luaopen_BankAccount(lua_State* _L) {
luaW_register<BankAccount>(_L,
"BankAccount",
BankAccount_table,
BankAccount_metatable,
BankAccount_new // If your class has a default constructor you can omit this argument,
// LuaWrapper will generate a default allocator for you.
);
return 1;
}

View File

@ -0,0 +1,7 @@
#ifndef LUAEXAMPLE_HPP_
#define LUAEXAMPLE_HPP_
struct lua_State;
int luaopen_BankAccount(lua_State* _L);
#endif // LUAEXAMPLE_HPP_

3
sample/example1/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
g++ -std=c++0x -L../lua-5.1/src -I../lua-5.1/src -I../luawrapper BankAccount.cpp LuaBankAccount.cpp main.cpp -llua -ldl -o example1

22
sample/example1/README Normal file
View File

@ -0,0 +1,22 @@
This folder includes a simple project to demonstrate how LuaWrapper may be used
to bind your C++ class to Lua.
main.cpp contains a simple main function that will call luaopen_BankAccount
then run the script given.
BankAccount.cpp and BankAccount.hpp contain a simple BankAccount class which you
can use to deposit and withdraw money from, and check the current balance.
LuaBankAccount.cpp contains the function wrappers. This is where the interesting
part is.
A visual studio project and makefile are provided, or you can use your own
tool chain to compile the example. Once compiled, run
example1.exe example1.lua
or
./example1 example1.lua
to see the code in action.

View File

@ -0,0 +1,22 @@
-- create a bank account and do some account action:
alicesaccount = BankAccount.new("Alice", 100)
alicesaccount:deposit(20);
alicesaccount:deposit(30);
alicesaccount:deposit(40);
bobsaccount = BankAccount.new("Bob", 200)
bobsaccount:withdraw(10);
bobsaccount:withdraw(15);
bobsaccount:withdraw(20);
-- create a function
function printaccountbalance(account)
local name = account:getOwnerName()
local balance = account:checkBalance()
print(string.format("%s has $%d", name, balance))
end
-- print for debug :
printaccountbalance(alicesaccount)
printaccountbalance(bobsaccount)
print(string.format("The bank has $%d", BankAccount.checkTotalMoneyInBank()))

View File

@ -0,0 +1,26 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
def get_desc():
return "luaWrapper example 1 : simple lua wrapper"
def create(target):
# module name is 'edn' and type binary.
myModule = module.Module(__file__, 'luaWrapperExample1', 'BINARY')
# add extra compilation flags :
myModule.add_extra_compile_flags()
# add the file to compile:
myModule.add_src_file([
'BankAccount.cpp',
'LuaBankAccount.cpp',
'main.cpp'])
# name of the dependency
myModule.add_module_depend('luaWrapper')
# add the currrent module at the
return myModule

22
sample/example1/main.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <iostream>
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>
#include "LuaBankAccount.hpp"
using namespace std;
int main(int _argc, const char *_argv[]) {
if (_argc == 2) {
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luaopen_BankAccount(L);
if (luaL_dofile(L, _argv[1])) {
std::cout << lua_tostring(L, -1) << std::endl;
}
lua_close(L);
}
return 0;
}

View File

@ -0,0 +1,95 @@
#include "Example.hpp"
#include <string>
#include <iostream>
// These are just a bunch of generic getters and setters for
// values in the Example class
bool Example::GetBoolean() const {
return m_boolean;
}
int Example::GetInteger() const {
return m_integer;
}
unsigned int Example::GetUInteger() const {
return m_uinteger;
}
const char* Example::GetCString() const {
return m_cstring;
}
const std::string& Example::GetCPPString() const {
return m_cppstring;
}
const Vector2D& Example::GetVec() const {
return m_vec;
}
double Example::GetNumber() const {
return m_number;
}
float Example::GetFloatNumber() const {
return m_floatnumber;
}
Example* Example::GetPtr() const {
return m_ptr;
}
void Example::SetBoolean(bool _val) {
m_boolean = _val;
}
void Example::SetInteger(int _val) {
m_integer = _val;
}
void Example::SetUInteger(unsigned int _val) {
m_uinteger = _val;
}
void Example::SetCString(const char* _val) {
m_cstring = _val;
}
void Example::SetCPPString(const std::string& _val) {
m_cppstring = _val;
}
void Example::SetVec(const Vector2D& _val) {
m_vec = _val;
}
void Example::SetNumber(double _val) {
m_number = _val;
}
void Example::SetFloatNumber(float _val) {
m_floatnumber = _val;
}
void Example::SetPtr(Example* _val) {
m_ptr = _val;
}
int Example::DoSomething(bool _b) {
std::cout << "b = " << _b << std::endl;
return 0;
}
int Example::DoSomethingElse(int _i, int _j) {
std::cout << "i = " << _i << ", j = " << _j << std::endl;
return 0;
}
int Example::DoSomethingElse(float _f) {
std::cout << "f = " << _f << std::endl;
return 0;
}

View File

@ -0,0 +1,60 @@
#ifndef EXAMPLE_HPP_
#define EXAMPLE_HPP_
#include <string>
#include "Vector2D.hpp"
class Example {
public:
Example() :
m_boolean(),
m_integer(),
m_uinteger(),
m_cstring(""),
m_cppstring(""),
m_number(),
m_floatnumber(),
m_ptr(),
m_vec() {
}
bool m_boolean;
int m_integer;
unsigned int m_uinteger;
const char* m_cstring;
std::string m_cppstring;
double m_number;
float m_floatnumber;
Example* m_ptr;
Vector2D m_vec;
bool GetBoolean() const;
int GetInteger() const;
unsigned int GetUInteger() const;
const char* GetCString() const;
const std::string& GetCPPString() const;
double GetNumber() const;
float GetFloatNumber() const;
Example* GetPtr() const;
const Vector2D& GetVec() const;
void SetBoolean(bool _val);
void SetInteger(int _val);
void SetUInteger(unsigned int _val);
void SetCString(const char* _val);
void SetCPPString(const std::string& _val);
void SetNumber(double _val);
void SetFloatNumber(float _val);
void SetPtr(Example* _val);
void SetVec(const Vector2D& _Member);
int DoSomething(bool _b);
void DoSomething2(bool _b, int _p) {};
int DoSomethingElse(int _i, int _j);
int DoSomethingElse(float _f);
};
#endif

View File

@ -0,0 +1,60 @@
#ifndef LUA_CUSTOM_TYPES_H__
#define LUA_CUSTOM_TYPES_H__
#include <string>
#include <lua/lua.h>
#include <luaWrapper/luaWrapper.hpp>
#include <luaWrapper/luaWrapperUtil.hpp>
#include "Vector2D.hpp"
/**
* LuaWrapper knows about primitive types like ints and floats, but it doesn't
* know about things like std::strings or other more complicated types.
* Sometimes, rather than register the type with LuaWrapper, it's easier to
* be able to convert it to and from Lua's primitive types, like strings or
* tables.
*
* To do this, you must write luaU_check, luaU_to and luaU_push functions for
* your type. You don't always need all three, it depends on if you're pushing
* objects to Lua, getting objects from Lua, or both.
*
* This example uses std::string, but if you have other custom string types it
* should be easy to write versions of those functions too
*/
template<> struct luaU_Impl<std::string> {
static std::string luaU_check(lua_State* _L, int _index) {
return std::string(luaL_checkstring(_L, _index));
}
static std::string luaU_to(lua_State* _L, int _index) {
return std::string(lua_tostring(_L, _index));
}
static void luaU_push(lua_State* _L, const std::string& _val) {
lua_pushstring(_L, _val.c_str());
}
};
/**
* These two functions let me convert a simple Vector2D structure into a Lua
* table holding the x and y values
*/
template<> struct luaU_Impl<Vector2D> {
static Vector2D luaU_check(lua_State* _L, int _index) {
return Vector2D(
luaU_getfield<float>(_L, _index, "x"),
luaU_getfield<float>(_L, _index, "y"));
}
static Vector2D luaU_to(lua_State* _L, int _index ) {
return Vector2D(
luaU_getfield<float>(_L, _index, "x"),
luaU_getfield<float>(_L, _index, "y"));
}
static void luaU_push(lua_State* _L, const Vector2D& _val) {
lua_newtable(_L);
luaU_setfield<float>(_L, -1, "x", _val.x);
luaU_setfield<float>(_L, -1, "y", _val.y);
}
};
#endif

View File

@ -0,0 +1,140 @@
#include <iostream>
#include <string>
#include <lua/lua.h>
#include <luaWrapper/luaWrapper.hpp>
#include <luaWrapper/luaWrapperUtil.hpp>
#include "LuaCustomTypes.hpp"
#include "Example.hpp"
static int Example_PrintMe(lua_State* L) {
Example* ex = luaW_check<Example>(L, 1);
std::cout << "Example="
<< "{m_boolean=" << ex->m_boolean
<< ",m_integer=" << ex->m_integer
<< ",m_uinteger=" << ex->m_uinteger
<< ",m_string='" << ex->m_cstring << "'"
<< ",m_cppstring='" << ex->m_cppstring << "'"
<< ",m_number=" << ex->m_number
<< ",m_floatnumber=" << ex->m_floatnumber
<< ",m_vec={x=" << ex->m_vec.x << ",y=" << ex->m_vec.y <<"}}" << std::endl;
return 0;
}
static luaL_Reg Example_metatable[] = {
// This function is
{ "PrintMe", Example_PrintMe },
// In many cases, all you need are simple getter or setter functions This
// was such a common pattern for me that I wrote a huge set of templates
// that generate these getter and setter functions for me. This is an
// example of how they can be used
//
// Because everything in the Example class is marked public, you can access
// them directly with these templates.
//
// Class | data type | class member
{ "GetBoolean", luaU_get<Example, bool, &Example::m_boolean> },
{ "SetBoolean", luaU_set<Example, bool, &Example::m_boolean> },
{ "Boolean", luaU_getset<Example, bool, &Example::m_boolean> },
{ "GetInteger", luaU_get<Example, int, &Example::m_integer> },
{ "SetInteger", luaU_set<Example, int, &Example::m_integer> },
{ "Integer", luaU_getset<Example, int, &Example::m_integer> },
{ "GetUInteger", luaU_get<Example, unsigned int, &Example::m_uinteger> },
{ "SetUInteger", luaU_set<Example, unsigned int, &Example::m_uinteger> },
{ "UInteger", luaU_getset<Example, unsigned int, &Example::m_uinteger> },
{ "GetCString", luaU_get<Example, const char*, &Example::m_cstring> },
{ "SetCString", luaU_set<Example, const char*, &Example::m_cstring> },
{ "CString", luaU_getset<Example, const char*, &Example::m_cstring> },
{ "GetCPPString", luaU_get<Example, std::string, &Example::m_cppstring> },
{ "SetCPPString", luaU_set<Example, std::string, &Example::m_cppstring> },
{ "CPPString", luaU_getset<Example, std::string, &Example::m_cppstring> },
{ "GetVec", luaU_get<Example, Vector2D, &Example::m_vec> },
{ "SetVec", luaU_set<Example, Vector2D, &Example::m_vec> },
{ "Vec", luaU_getset<Example, Vector2D, &Example::m_vec> },
{ "GetNumber", luaU_get<Example, double, &Example::m_number> },
{ "SetNumber", luaU_set<Example, double, &Example::m_number> },
{ "Number", luaU_getset<Example, double, &Example::m_number> },
{ "GetFloatNumber", luaU_get<Example, float, &Example::m_floatnumber> },
{ "SetFloatNumber", luaU_set<Example, float, &Example::m_floatnumber> },
{ "FloatNumber", luaU_getset<Example, float, &Example::m_floatnumber> },
{ "GetPtr", luaU_get<Example, Example, &Example::m_ptr> },
{ "SetPtr", luaU_set<Example, Example, &Example::m_ptr> },
{ "Ptr", luaU_getset<Example, Example, &Example::m_ptr> },
// The getters and setters above work on member variables directly, but
// sometimes all you have are getter and setter functions instead of
// variables. You can still automate the creation of Getter and Setter Lua
// function wrappers.
// Normally it would be silly to have getter and setter functions for both
// the member variable AND the getter/setter function, I've included both
// here as an example of how it works
// Example of member access through getters and setters
// Class | data type | getter
// Class | data type | setter
// Class | data type | getter | setter
{ "GetBooleanFunc", luaU_get<Example, bool, &Example::GetBoolean> },
{ "SetBooleanFunc", luaU_set<Example, bool, &Example::SetBoolean> },
{ "BooleanFunc", luaU_getset<Example, bool, &Example::GetBoolean, &Example::SetBoolean> },
{ "GetIntegerFunc", luaU_get<Example, int, &Example::GetInteger> },
{ "SetIntegerFunc", luaU_set<Example, int, &Example::SetInteger> },
{ "IntegerFunc", luaU_getset<Example, int, &Example::GetInteger, &Example::SetInteger> },
{ "GetUIntegerFunc", luaU_get<Example, unsigned int, &Example::GetUInteger> },
{ "SetUIntegerFunc", luaU_set<Example, unsigned int, &Example::SetUInteger> },
{ "UIntegerFunc", luaU_getset<Example, unsigned int, &Example::GetUInteger, &Example::SetUInteger> },
{ "GetCStringFunc", luaU_get<Example, const char*, &Example::GetCString> },
{ "SetCStringFunc", luaU_set<Example, const char*, &Example::SetCString> },
{ "CStringFunc", luaU_getset<Example, const char*, &Example::GetCString, &Example::SetCString> },
{ "GetNumberFunc", luaU_get<Example, double, &Example::GetNumber> },
{ "SetNumberFunc", luaU_set<Example, double, &Example::SetNumber> },
{ "NumberFunc", luaU_getset<Example, double, &Example::GetNumber, &Example::SetNumber> },
{ "GetFloatNumberFunc", luaU_get<Example, float, &Example::GetFloatNumber> },
{ "SetFloatNumberFunc", luaU_set<Example, float, &Example::SetFloatNumber> },
{ "FloatNumberFunc", luaU_getset<Example, float, &Example::GetFloatNumber, &Example::SetFloatNumber> },
{ "GetPtrFunc", luaU_get<Example, Example, &Example::GetPtr> },
{ "SetPtrFunc", luaU_set<Example, Example, &Example::SetPtr> },
{ "PtrFunc", luaU_getset<Example, Example, &Example::GetPtr, &Example::SetPtr> },
// In order to use luaU_get and luaU_set on non-primitive types, you must define luaU_to
// and luaU_check for that type.
// See LuaCustomTypes.hpp for an example involving std::string and Vector2D
{ "GetCPPStringFunc", luaU_get<Example, std::string, &Example::GetCPPString> },
{ "SetCPPStringFunc", luaU_set<Example, std::string, &Example::SetCPPString> },
{ "CPPStringFunc", luaU_getset<Example, std::string, &Example::GetCPPString, &Example::SetCPPString> },
{ "GetVecFunc", luaU_get<Example, Vector2D, &Example::GetVec> },
{ "SetSetFunc", luaU_set<Example, Vector2D, &Example::SetVec> },
{ "VecFunc", luaU_getset<Example, Vector2D, &Example::GetVec, &Example::SetVec> },
{ "DoSomething", luaU_func(&Example::DoSomething) },
{ "DoSomething2", luaU_func(&Example::DoSomething2) },
//{ "DoSomethingElse1", luaU_funcsig(int, Example, DoSomethingElse, int, int) },
//{ "DoSomethingElse2", luaU_funcsig(int, Example, DoSomethingElse, float) },
{ NULL, NULL }
};
int luaopen_Example(lua_State* L)
{
luaW_register<Example>(L, "Example", NULL, Example_metatable);
return 1;
}

View File

@ -0,0 +1,7 @@
#ifndef LUAEXAMPLE_HPP_
#define LUAEXAMPLE_HPP_
struct lua_State;
int luaopen_Example(lua_State* _L);
#endif

3
sample/example2/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
g++ -std=c++0x -L../lua-5.1/src -I../lua-5.1/src -I../luawrapper Example.cpp LuaExample.cpp main.cpp -llua -ldl -o example2

66
sample/example2/README Normal file
View File

@ -0,0 +1,66 @@
This folder includes a project that demonstrates some of the more sophisticated
and complex features of LuaWrapper. Make sure you fully understand the code in
example 1 before reading the code here. Example 2 introduces some of the
features contained in the luawrapperutil.hpp file which are prefixed with luaU_
instead of luaW_
# Automatic Getter and Setter Generation
In example 1, all of the wrapper functions were written manually. It is very
common need to write get or set function wrappers - so common in fact that this
patter was extracted out into a templated function. In example 1, BankAccount
had the following wrapper:
int BankAccount_checkBalance(lua_State *L)
{
BankAccount* account = luaW_check<BankAccount>(L, 1);
lua_pushnumber(L, account->checkBalance());
return 1;
}
static luaL_Reg BankAccount_metatable[] =
{
...
{ "checkBalance", BankAccount_checkBalance },
...
};
Rather than write code like that every time you need to create a get function,
you can instead just do this:
{ "checkBalance", luaU_get<BankAccount, float, &BankAccount::checkBalance },
This code will automatically instantiate a function wrapper that returns a
float, the result of a call to checkBalance.
# Generic push, to and check functions
Normally, when you need to push a value to Lua or get a value from the stack,
you use a function like lua_pushnumber or lua_tonumber. luawrapperutil.hpp
contains a generic version of these functions as well as specializations for
the primitive types in C++. That means you can do things like this:
int i = luaU_check<int>(L, 1);
luaU_push<double>(L, 1.234);
Additionally, it is possible to extend this functionality to non-primitive
types. A common use is to create a wrapper for std::strings so that you may do
this:
std::string str1 = luaU_check<std::string>(L, 1);
std::string str2 = "Lua is awesome!"
luaU_push<std::string>(L, str2);
Examples of how to create your own luaU_push, luaU_to, and luaU_check can be
found in LuaCustomTypes.hpp
A visual studio project and makefile are provided, or you can use your own
tool chain to compile the example. Once compiled, run
example2.exe example2.lua
or
./example2 example2.lua
to see the code in action.

View File

@ -0,0 +1,14 @@
#ifndef VECTOR2D_H_
#define VECTOR2D_H_
struct Vector2D {
Vector2D(float x_ = 0.f, float y_ = 0.f) :
x(x_),
y(y_) {
}
float x;
float y;
};
#endif

View File

@ -0,0 +1,27 @@
local ex = Example.new()
ex:SetBoolean(true)
-- luaU_getset creates a function that acts as both a getter and a setter!
-- For example, I can use a single function, Integer, to set the value to 100
-- then print out that value
ex:Integer(100)
print("Integer = ", ex:Integer());
ex:SetCPPString("This will be automatically conveted into a std::string")
-- Because I've defined helper functions for Vector2D, I can pass this table
-- to a function expecting a Vector2d as an argument. I don't need to register
-- a Vector2D type with luaW_register - this table will automatically be converted
vec = { x = 20; y = 30 }
ex:SetVec(vec)
vec2 = ex:GetVec()
print "Printing values from the vector stored on `ex`:"
for k, v in pairs(vec2) do
print(k, v)
end
ex:PrintMe()

View File

@ -0,0 +1,26 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
def get_desc():
return "luaWrapper example 2 : simple lua wrapper"
def create(target):
# module name is 'edn' and type binary.
myModule = module.Module(__file__, 'luaWrapperExample2', 'BINARY')
# add extra compilation flags :
myModule.add_extra_compile_flags()
# add the file to compile:
myModule.add_src_file([
'Example.cpp',
'LuaExample.cpp',
'main.cpp'])
# name of the dependency
myModule.add_module_depend('luaWrapper')
# add the currrent module at the
return myModule

20
sample/example2/main.cpp Normal file
View File

@ -0,0 +1,20 @@
#include <iostream>
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>
#include "LuaExample.hpp"
int main(int argc, const char *argv[]) {
if (argc == 2) {
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luaopen_Example(L);
if (luaL_dofile(L, argv[1])) {
std::cout << lua_tostring(L, -1) << std::endl;
}
lua_close(L);
}
return 0;
}