commit 11bbc0e7a101c3473894cbf732ea361bec0b7609 Author: Edouard DUPIN Date: Tue Dec 16 22:21:37 2014 +0100 [DEV] import basic c++ lua wrapper diff --git a/README.md b/README.md new file mode 100644 index 0000000..65eede6 --- /dev/null +++ b/README.md @@ -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 + luaW_to + luaW_check + luaW_push + luaW_register + luaW_extend + luaW_hold + luaW_release + +The first four functions allow you to manipulate arbitrary classes just like +you would the primitive types (e.g. numbers or strings). luaW_is 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 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, 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. diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..4a788db --- /dev/null +++ b/license.txt @@ -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. diff --git a/luaWrapper/luaWrapper.hpp b/luaWrapper/luaWrapper.hpp new file mode 100644 index 0000000..605f548 --- /dev/null +++ b/luaWrapper/luaWrapper.hpp @@ -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 + * luaW_to + * luaW_check + * luaW_push + * luaW_register + * luaW_setfuncs + * luaW_extend + * luaW_hold + * luaW_release + * + * 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 +#include + +#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 T* luaW_defaultallocator(lua_State*) { + return new T(); +} + +template 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 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 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 const char* LuaWrapper::classname; +template void (*LuaWrapper::identifier)(lua_State*, T*); +template T* (*LuaWrapper::allocator)(lua_State*); +template void (*LuaWrapper::deallocator)(lua_State*, T*); +template luaW_Userdata (*LuaWrapper::cast)(const luaW_Userdata&); +template void (*LuaWrapper::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(L). This is only used + * internally. + */ +template luaW_Userdata luaW_cast(const luaW_Userdata& _obj) { + return luaW_Userdata(static_cast(static_cast(_obj.data)), LuaWrapper::cast); +} + +template void luaW_identify(lua_State* _L, T* _obj) { + LuaWrapper::identifier(_L, static_cast(_obj)); +} + +template 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::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 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::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 T* luaW_to(lua_State* _L, int _index, bool _strict = false) { + if (luaW_is(_L, _index, _strict)) { + luaW_Userdata* pud = static_cast(lua_touserdata(_L, _index)); + luaW_Userdata ud; + while ( !_strict + && LuaWrapper::cast != pud->cast) { + ud = pud->cast(*pud); + pud = &ud; + } + return static_cast(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 T* luaW_check(lua_State* _L, int _index, bool _strict = false) { + T* obj = NULL; + if (luaW_is(_L, _index, _strict)) { + luaW_Userdata* pud = static_cast(lua_touserdata(_L, _index)); + luaW_Userdata ud; + while (!_strict && LuaWrapper::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::classname, luaL_typename(_L, _index)); + luaL_argerror(_L, _index, msg); + } + return obj; +} + +template 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(_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 void luaW_push(lua_State* _L, T* _obj) { + if (_obj) { + LuaWrapper::identifier(_L, _obj); // ... id + luaW_wrapperfield(_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(lua_newuserdata(_L, sizeof(luaW_Userdata))); // ... cache id obj + ud->data = _obj; + ud->cast = LuaWrapper::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::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 bool luaW_hold(lua_State* _L, T* _obj) { + luaW_wrapperfield(_L, LUAW_HOLDS_KEY); // ... holds + LuaWrapper::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 void luaW_release(lua_State* _L, int _index) { + luaW_wrapperfield(_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 void luaW_release(lua_State* _L, T* _obj) { + LuaWrapper::identifier(_L, _obj); // ... id + luaW_release(_L, -1); // ... id + lua_pop(_L, 1); // ... +} + +template void luaW_postconstructorinternal(lua_State* _L, int _numargs) { + // ... ud args... + if (LuaWrapper::postconstructorrecurse) { + LuaWrapper::postconstructorrecurse(_L, _numargs); + } + luaL_getmetatable(_L, LuaWrapper::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 void luaW_postconstructor(lua_State* _L, int _numargs) { + // ... ud args... + luaW_postconstructorinternal(_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 inline int luaW_new(lua_State* _L, int _numargs) { + // ... args... + T* obj = LuaWrapper::allocator(_L); + luaW_push(_L, obj); // ... args... ud + luaW_hold(_L, obj); + lua_insert(_L, -1 - _numargs); // ... ud args... + luaW_postconstructor(_L, _numargs); // ... ud + return 1; +} + +template int luaW_new(lua_State* _L) { + return luaW_new(_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 int luaW_index(lua_State* _L) { + // obj key + T* obj = luaW_to(_L, 1); + luaW_wrapperfield(_L, LUAW_STORAGE_KEY); // obj key storage + LuaWrapper::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 int luaW_newindex(lua_State* _L) { + // obj key value + T* obj = luaW_check(_L, 1); + luaW_wrapperfield(_L, LUAW_STORAGE_KEY); // obj key value storage + LuaWrapper::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 int luaW_gc(lua_State* _L) { + // obj + T* obj = luaW_to(_L, 1); + LuaWrapper::identifier(_L, obj); // obj key value storage id + luaW_wrapperfield(_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::deallocator) { + LuaWrapper::deallocator(_L, obj); + } + luaW_wrapperfield(_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(_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 void luaW_setfuncs(lua_State* _L, + const char* _classname, + const luaL_Reg* _table, + const luaL_Reg* _metatable, + T* (*_allocator)(lua_State*) = luaW_defaultallocator, + void (*_deallocator)(lua_State*, T*) = luaW_defaultdeallocator, + void (*_identifier)(lua_State*, T*) = luaW_defaultidentifier) { + luaW_initialize(_L); + LuaWrapper::classname = _classname; + LuaWrapper::identifier = _identifier; + LuaWrapper::allocator = _allocator; + LuaWrapper::deallocator = _deallocator; + const luaL_Reg defaulttable[] = { + { "new", luaW_new }, + { NULL, NULL } + }; + const luaL_Reg defaultmetatable[] = { + { "__index", luaW_index }, + { "__newindex", luaW_newindex }, + { "__gc", luaW_gc }, + { 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::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::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(_L, LUAW_CACHE_METATABLE_KEY); // ... LuaWrapper LuaWrapper.cache {} cmt + lua_setmetatable(_L, -2); // ... LuaWrapper LuaWrapper.cache {} + lua_setfield(_L, -2, LuaWrapper::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 void luaW_register(lua_State* _L, + const char* _classname, + const luaL_Reg* _table, + const luaL_Reg* _metatable, + T* (*_allocator)(lua_State*) = luaW_defaultallocator, + void (*_deallocator)(lua_State*, T*) = luaW_defaultdeallocator, + void (*_identifier)(lua_State*, T*) = luaW_defaultidentifier) { + 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 to cast your object apropriately, as + * casts straight through a void pointer do not work. + */ +template void luaW_extend(lua_State* _L) { + if(!LuaWrapper::classname) { + luaL_error(_L, "attempting to call extend on a type that has not been registered"); + } + if(!LuaWrapper::classname) { + luaL_error(_L, "attempting to extend %s by a type that has not been registered", LuaWrapper::classname); + } + LuaWrapper::cast = luaW_cast; + LuaWrapper::identifier = luaW_identify; + LuaWrapper::postconstructorrecurse = luaW_postconstructorinternal; + luaL_getmetatable(_L, LuaWrapper::classname); // mt + luaL_getmetatable(_L, LuaWrapper::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::classname); // ... LuaWrapper LuaWrapper.storage U + lua_setfield(_L, -2, LuaWrapper::classname); // ... LuaWrapper LuaWrapper.storage + lua_pop(_L, 1); // ... LuaWrapper + lua_getfield(_L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds + lua_getfield(_L, -1, LuaWrapper::classname); // ... LuaWrapper LuaWrapper.holds U + lua_setfield(_L, -2, LuaWrapper::classname); // ... LuaWrapper LuaWrapper.holds + lua_pop(_L, 1); // ... LuaWrapper + lua_getfield(_L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache + lua_getfield(_L, -1, LuaWrapper::classname); // ... LuaWrapper LuaWrapper.cache U + lua_setfield(_L, -2, LuaWrapper::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::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 diff --git a/luaWrapper/luaWrapperUtil.hpp b/luaWrapper/luaWrapperUtil.hpp new file mode 100644 index 0000000..eb767ad --- /dev/null +++ b/luaWrapper/luaWrapperUtil.hpp @@ -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 + +#include + +#ifndef LUAW_STD +#define LUAW_STD std +#endif + +/** + * @brief This template removes reference and const qualifier from the type + */ +template struct luaW_remove_cr { + typedef typename std::remove_const::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 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 U luaU_check(lua_State* _L, int _index) { + return luaU_Impl::luaU_check(_L, _index); +} +template U luaU_to(lua_State* _L, int _index) { + return luaU_Impl::luaU_to(_L, _index); +} +template void luaU_push(lua_State* _L, const U& _value) { + luaU_Impl::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 U luaU_opt(lua_State* _L, int _index, const U& _fallback = U()) { + if (lua_isnil(_L, _index)) + return _fallback; + else + return luaU_Impl::luaU_check(_L, _index); +} + +template<> struct luaU_Impl { + 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 { + 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 { + static unsigned int luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checkinteger(_L, _index)); + } + static unsigned int luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tointeger(_L, _index)); + } + static void luaU_push(lua_State* _L, const unsigned int& _value) { + lua_pushinteger(_L, _value); + } +}; + +template<> struct luaU_Impl { + static int luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checkinteger(_L, _index)); + } + static int luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tointeger(_L, _index)); + } + static void luaU_push(lua_State* _L, const int& _value) { + lua_pushinteger(_L, _value); + } +}; + +template<> struct luaU_Impl { + static unsigned char luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checkinteger(_L, _index)); + } + static unsigned char luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tointeger(_L, _index)); + } + static void luaU_push(lua_State* _L, const unsigned char& _value) { + lua_pushinteger(_L, _value); + } +}; + +template<> struct luaU_Impl { + static char luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checkinteger(_L, _index)); + } + static char luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tointeger(_L, _index)); + } + static void luaU_push(lua_State* _L, const char& _value) { + lua_pushinteger(_L, _value); + } +}; + +template<> struct luaU_Impl { + static float luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checknumber(_L, _index)); + } + static float luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tonumber(_L, _index)); + } + static void luaU_push(lua_State* _L, const float& _value) { + lua_pushnumber(_L, _value); + } +}; + +template<> struct luaU_Impl { + static double luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checknumber(_L, _index)); + } + static double luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tonumber(_L, _index)); + } + static void luaU_push(lua_State* _L, const double& _value) { + lua_pushnumber(_L, _value); + } +}; + +template struct luaU_Impl::value>::type> { + static T luaU_check(lua_State* _L, int _index) { + return static_cast(luaL_checkinteger(_L, _index)); + } + static T luaU_to(lua_State* _L, int _index) { + return static_cast(lua_tointeger(_L, _index)); + } + static void luaU_push(lua_State* _L, const T& _value) { + lua_pushnumber(_L, static_cast(_value)); + } +}; + +template struct luaU_Impl::value>::type> { + static T* luaU_check(lua_State* _L, int _index) { + return luaW_check(_L, _index); + } + static T* luaU_to(lua_State* _L, int _index) { + return luaW_to(_L, _index); + } + static void luaU_push(lua_State* _L, T*& _value) { + luaW_push (_L, _value); + } + static void luaU_push(lua_State* _L, T* _value) { + luaW_push (_L, _value); + } +}; + +/** + * These are just some functions I've always felt should exist + */ +template inline U luaU_getfield(lua_State* _L, int _index, const char* _field) { + static_assert(!LUAW_STD::is_same::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(_L, -1); + lua_pop(_L, 1); + return val; +} + +template inline U luaU_checkfield(lua_State* _L, int _index, const char* _field) { + static_assert(!LUAW_STD::is_same::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(_L, -1); + lua_pop(_L, 1); + return val; +} + +template inline U luaU_optfield(lua_State* _L, int _index, const char* _field, const U& _fallback = U()) { + static_assert(!LUAW_STD::is_same::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(_L, -1, _fallback); + lua_pop(_L, 1); + return val; +} + +template inline void luaU_setfield(lua_State* _L, int _index, const char* _field, U _val) { + luaU_push(_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 }, + * { "SetBar", luaU_set }, + * { "Bar", luaU_getset }, + * { 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 }, + * { "SetBar", luaU_set }, + * { "Bar", luaU_getset }, + * } + * + * In a Lua script, you can now use foo:GetBar(), foo:SetBar() and foo:Bar() + */ + +template int luaU_get(lua_State* _L) { + T* obj = luaW_check(_L, 1); + luaU_push(_L, obj->*Member); + return 1; +} + +template int luaU_get(lua_State* _L) { + T* obj = luaW_check(_L, 1); + luaW_push(_L, obj->*Member); + return 1; +} + +template int luaU_get(lua_State* _L) { + T* obj = luaW_check(_L, 1); + luaU_push(_L, (obj->*Getter)()); + return 1; +} + +template int luaU_get(lua_State* _L) { + T* obj = luaW_check(_L, 1); + luaU_push(_L, (obj->*Getter)()); + return 1; +} + +template int luaU_get(lua_State* _L) { + T* obj = luaW_check(_L, 1); + luaW_push(_L, (obj->*Getter)()); + return 1; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + obj->*Member = luaU_check(_L, 2); + } + return 0; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + U* member = luaW_opt(_L, 2); + obj->*Member = member; + } + return 0; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + U* member = luaW_opt(_L, 2); + obj->*Member = member; + } + return 0; +} + +template int luaU_setandrelease(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + U* member = luaW_opt(_L, 2); + obj->*Member = member; + if (member) { + luaW_release(_L, member); + } + } + return 0; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + (obj->*Setter)(luaU_check(_L, 2)); + } + return 0; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + (obj->*Setter)(luaU_check(_L, 2)); + } + return 0; +} + +template int luaU_set(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + U* member = luaW_opt(_L, 2); + (obj->*Setter)(member); + } + return 0; +} + +template int luaU_setandrelease(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj) { + U* member = luaW_opt(_L, 2); + (obj->*Setter)(member); + if (member) { + luaW_release(_L, member); + } + } + return 0; +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + obj->*Member = luaU_check(_L, 2); + return 0; + } else { + luaU_push(_L, obj->*Member); + return 1; + } +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + U* member = luaW_opt(_L, 2); + obj->*Member = member; + return 0; + } else { + luaW_push(_L, obj->*Member); + return 1; + } +} + +template int luaU_getsetandrelease(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + U* member = luaW_opt(_L, 2); + obj->*Member = member; + if (member) + luaW_release(_L, member); + return 0; + } else { + luaW_push(_L, obj->*Member); + return 1; + } +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + (obj->*Setter)(luaU_check(_L, 2)); + return 0; + } else { + luaU_push(_L, (obj->*Getter)()); + return 1; + } +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + (obj->*Setter)(luaU_check(_L, 2)); + return 0; + } else { + luaU_push(_L, (obj->*Getter)()); + return 1; + } +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + (obj->*Setter)(luaU_check(_L, 2)); + return 0; + } else { + luaU_push(_L, (obj->*Getter)()); + return 1; + } +} + +template int luaU_getset(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + U* member = luaW_opt(_L, 2); + (obj->*Setter)(member); + return 0; + } else { + luaW_push(_L, (obj->*Getter)()); + return 1; + } +} + +template int luaU_getsetandrelease(lua_State* _L) { + T* obj = luaW_check(_L, 1); + if (obj && lua_gettop(_L) >= 2) { + U* member = luaW_opt(_L, 2); + (obj->*Setter)(member); + if (member) + luaW_release(_L, member); + return 0; + } else { + luaW_push(_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(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(L, 1)->DoSomething(luaU_check(L, 2), luaU_check(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(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::call +#define luaU_funcsig(returntype, type, funcname, ...) luaU_func(static_cast(&type::funcname)) + +#define luaU_staticfunc(func) &luaU_StaticFuncWrapper::call +#define luaU_staticfuncsig(returntype, type, funcname, ...) luaU_staticfunc(static_cast(&type::funcname)) + +template struct luaU_IntPack { }; +template struct luaU_MakeIntRangeType { + typedef typename luaU_MakeIntRangeType::type type; +}; +template struct luaU_MakeIntRangeType { + typedef luaU_IntPack type; +}; +template inline typename luaU_MakeIntRangeType::type luaU_makeIntRange() { + return typename luaU_MakeIntRangeType::type(); +} + +/** + * Member function wrapper + */ +template struct luaU_MemberFuncWrapper; + +template struct luaU_MemberFuncWrapper { + public: + static int call(lua_State* _L) { + return callImpl(_L, luaU_makeIntRange<2,sizeof...(Args)>()); + } + private: + template static int callImpl(lua_State* _L, luaU_IntPack) { + luaU_push(_L, (luaW_check(_L, 1)->*MemberFunc)(luaU_check::type>(_L, indices)...)); + return 1; + } +}; + +template struct luaU_MemberFuncWrapper { + public: + static int call(lua_State* _L) { + return callImpl(_L, luaU_makeIntRange<2, sizeof...(Args)>()); + } + private: + template static int callImpl(lua_State* _L, luaU_IntPack) { + (luaW_check(_L, 1)->*MemberFunc)(luaU_check::type>(_L, indices)...); + return 0; + } +}; + + +/** + * static function wrapper + */ + +template struct luaU_StaticFuncWrapper; + +template struct luaU_StaticFuncWrapper { + public: + static int call(lua_State* _L) { + return callImpl(_L, luaU_makeIntRange<2,sizeof...(Args)>()); + } + private: + template static int callImpl(lua_State* _L, luaU_IntPack) { + luaU_push(_L, (*Func)(luaU_check::type>(_L, indices)...)); + return 1; + } +}; + +template struct luaU_StaticFuncWrapper { + public: + static int call(lua_State* _L) { + return callImpl(_L, luaU_makeIntRange<2, sizeof...(Args)>()); + } + private: + template + static int callImpl(lua_State* _L, luaU_IntPack) { + (*Func)(luaU_check::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 }, + * { NULL, NULL } + * }; + * + * Lua: + * foo = Foo.new() + * foo2 = foo:clone() + */ +template int luaU_clone(lua_State* _L) { + T* obj = new T(*luaW_check(_L, 1)); + lua_remove(_L, 1); // ... + int numargs = lua_gettop(_L); + luaW_push(_L, obj); // ... clone + luaW_hold(_L, obj); + luaW_postconstructor(_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 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(_L, 1); + * Bar* child = luaW_check(_L, 2); + * if (parent && child) + * { + * luaU_store(_L, 1, "Children"); + * parent->AddChild(child); + * } + */ +template 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::identifier(_L, luaW_to(_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 diff --git a/lutin_luaWrapper.py b/lutin_luaWrapper.py new file mode 100644 index 0000000..91958eb --- /dev/null +++ b/lutin_luaWrapper.py @@ -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 + + diff --git a/sample/README b/sample/README new file mode 100644 index 0000000..bd9658c --- /dev/null +++ b/sample/README @@ -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/ + diff --git a/sample/example1/BankAccount.cpp b/sample/example1/BankAccount.cpp new file mode 100644 index 0000000..9c39557 --- /dev/null +++ b/sample/example1/BankAccount.cpp @@ -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; +} + diff --git a/sample/example1/BankAccount.hpp b/sample/example1/BankAccount.hpp new file mode 100644 index 0000000..4fd6a95 --- /dev/null +++ b/sample/example1/BankAccount.hpp @@ -0,0 +1,21 @@ +#ifndef EXAMPLE_HPP_ +#define EXAMPLE_HPP_ + +#include +#include + +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_ diff --git a/sample/example1/LuaBankAccount.cpp b/sample/example1/LuaBankAccount.cpp new file mode 100644 index 0000000..4533d4c --- /dev/null +++ b/sample/example1/LuaBankAccount.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include +#include +#include + +#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(_L, 1); + lua_pushstring(_L, account->getOwnerName()); + return 1; +} + +int BankAccount_deposit(lua_State* _L) { + BankAccount* account = luaW_check(_L, 1); + float amount = luaL_checknumber(_L, 2); + account->deposit(amount); + return 0; +} + +int BankAccount_withdraw(lua_State* _L) { + BankAccount* account = luaW_check(_L, 1); + float amount = luaL_checknumber(_L, 2); + account->withdraw(amount); + return 0; +} + +int BankAccount_checkBalance(lua_State* _L) { + BankAccount* account = luaW_check(_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(_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; +} + diff --git a/sample/example1/LuaBankAccount.hpp b/sample/example1/LuaBankAccount.hpp new file mode 100644 index 0000000..098e5e9 --- /dev/null +++ b/sample/example1/LuaBankAccount.hpp @@ -0,0 +1,7 @@ +#ifndef LUAEXAMPLE_HPP_ +#define LUAEXAMPLE_HPP_ + +struct lua_State; +int luaopen_BankAccount(lua_State* _L); + +#endif // LUAEXAMPLE_HPP_ diff --git a/sample/example1/Makefile b/sample/example1/Makefile new file mode 100644 index 0000000..ba63d7b --- /dev/null +++ b/sample/example1/Makefile @@ -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 diff --git a/sample/example1/README b/sample/example1/README new file mode 100644 index 0000000..865ba22 --- /dev/null +++ b/sample/example1/README @@ -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. diff --git a/sample/example1/example1.lua b/sample/example1/example1.lua new file mode 100644 index 0000000..4e10078 --- /dev/null +++ b/sample/example1/example1.lua @@ -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())) diff --git a/sample/example1/lutin_luaWrapperExample1.py b/sample/example1/lutin_luaWrapperExample1.py new file mode 100644 index 0000000..985180c --- /dev/null +++ b/sample/example1/lutin_luaWrapperExample1.py @@ -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 + + diff --git a/sample/example1/main.cpp b/sample/example1/main.cpp new file mode 100644 index 0000000..ee603a4 --- /dev/null +++ b/sample/example1/main.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include + + +#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; +} diff --git a/sample/example2/Example.cpp b/sample/example2/Example.cpp new file mode 100644 index 0000000..a72a5f1 --- /dev/null +++ b/sample/example2/Example.cpp @@ -0,0 +1,95 @@ +#include "Example.hpp" +#include +#include + +// 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; +} + + diff --git a/sample/example2/Example.hpp b/sample/example2/Example.hpp new file mode 100644 index 0000000..b4aed63 --- /dev/null +++ b/sample/example2/Example.hpp @@ -0,0 +1,60 @@ +#ifndef EXAMPLE_HPP_ +#define EXAMPLE_HPP_ + +#include + +#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 \ No newline at end of file diff --git a/sample/example2/LuaCustomTypes.hpp b/sample/example2/LuaCustomTypes.hpp new file mode 100644 index 0000000..71a6887 --- /dev/null +++ b/sample/example2/LuaCustomTypes.hpp @@ -0,0 +1,60 @@ +#ifndef LUA_CUSTOM_TYPES_H__ +#define LUA_CUSTOM_TYPES_H__ + +#include + +#include +#include +#include + +#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 { + 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 { + static Vector2D luaU_check(lua_State* _L, int _index) { + return Vector2D( + luaU_getfield(_L, _index, "x"), + luaU_getfield(_L, _index, "y")); + } + static Vector2D luaU_to(lua_State* _L, int _index ) { + return Vector2D( + luaU_getfield(_L, _index, "x"), + luaU_getfield(_L, _index, "y")); + } + static void luaU_push(lua_State* _L, const Vector2D& _val) { + lua_newtable(_L); + luaU_setfield(_L, -1, "x", _val.x); + luaU_setfield(_L, -1, "y", _val.y); + } +}; + +#endif \ No newline at end of file diff --git a/sample/example2/LuaExample.cpp b/sample/example2/LuaExample.cpp new file mode 100644 index 0000000..be083aa --- /dev/null +++ b/sample/example2/LuaExample.cpp @@ -0,0 +1,140 @@ +#include +#include + +#include + +#include +#include + +#include "LuaCustomTypes.hpp" +#include "Example.hpp" + +static int Example_PrintMe(lua_State* L) { + Example* ex = luaW_check(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 }, + { "SetBoolean", luaU_set }, + { "Boolean", luaU_getset }, + + { "GetInteger", luaU_get }, + { "SetInteger", luaU_set }, + { "Integer", luaU_getset }, + + { "GetUInteger", luaU_get }, + { "SetUInteger", luaU_set }, + { "UInteger", luaU_getset }, + + { "GetCString", luaU_get }, + { "SetCString", luaU_set }, + { "CString", luaU_getset }, + + { "GetCPPString", luaU_get }, + { "SetCPPString", luaU_set }, + { "CPPString", luaU_getset }, + + { "GetVec", luaU_get }, + { "SetVec", luaU_set }, + { "Vec", luaU_getset }, + + { "GetNumber", luaU_get }, + { "SetNumber", luaU_set }, + { "Number", luaU_getset }, + + { "GetFloatNumber", luaU_get }, + { "SetFloatNumber", luaU_set }, + { "FloatNumber", luaU_getset }, + + { "GetPtr", luaU_get }, + { "SetPtr", luaU_set }, + { "Ptr", luaU_getset }, + + // 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 }, + { "SetBooleanFunc", luaU_set }, + { "BooleanFunc", luaU_getset }, + + { "GetIntegerFunc", luaU_get }, + { "SetIntegerFunc", luaU_set }, + { "IntegerFunc", luaU_getset }, + + { "GetUIntegerFunc", luaU_get }, + { "SetUIntegerFunc", luaU_set }, + { "UIntegerFunc", luaU_getset }, + + { "GetCStringFunc", luaU_get }, + { "SetCStringFunc", luaU_set }, + { "CStringFunc", luaU_getset }, + + { "GetNumberFunc", luaU_get }, + { "SetNumberFunc", luaU_set }, + { "NumberFunc", luaU_getset }, + + { "GetFloatNumberFunc", luaU_get }, + { "SetFloatNumberFunc", luaU_set }, + { "FloatNumberFunc", luaU_getset }, + + { "GetPtrFunc", luaU_get }, + { "SetPtrFunc", luaU_set }, + { "PtrFunc", luaU_getset }, + + // 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 }, + { "SetCPPStringFunc", luaU_set }, + { "CPPStringFunc", luaU_getset }, + + { "GetVecFunc", luaU_get }, + { "SetSetFunc", luaU_set }, + { "VecFunc", luaU_getset }, + + { "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(L, "Example", NULL, Example_metatable); + return 1; +} + diff --git a/sample/example2/LuaExample.hpp b/sample/example2/LuaExample.hpp new file mode 100644 index 0000000..732f640 --- /dev/null +++ b/sample/example2/LuaExample.hpp @@ -0,0 +1,7 @@ +#ifndef LUAEXAMPLE_HPP_ +#define LUAEXAMPLE_HPP_ + +struct lua_State; +int luaopen_Example(lua_State* _L); + +#endif diff --git a/sample/example2/Makefile b/sample/example2/Makefile new file mode 100644 index 0000000..d54439c --- /dev/null +++ b/sample/example2/Makefile @@ -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 diff --git a/sample/example2/README b/sample/example2/README new file mode 100644 index 0000000..a46dd69 --- /dev/null +++ b/sample/example2/README @@ -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(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(L, 1); +luaU_push(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(L, 1); +std::string str2 = "Lua is awesome!" +luaU_push(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. diff --git a/sample/example2/Vector2D.hpp b/sample/example2/Vector2D.hpp new file mode 100644 index 0000000..53ee6b2 --- /dev/null +++ b/sample/example2/Vector2D.hpp @@ -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 \ No newline at end of file diff --git a/sample/example2/example2.lua b/sample/example2/example2.lua new file mode 100644 index 0000000..9342b27 --- /dev/null +++ b/sample/example2/example2.lua @@ -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() \ No newline at end of file diff --git a/sample/example2/lutin_luaWrapperExample2.py b/sample/example2/lutin_luaWrapperExample2.py new file mode 100644 index 0000000..e5dce73 --- /dev/null +++ b/sample/example2/lutin_luaWrapperExample2.py @@ -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 + + diff --git a/sample/example2/main.cpp b/sample/example2/main.cpp new file mode 100644 index 0000000..e388055 --- /dev/null +++ b/sample/example2/main.cpp @@ -0,0 +1,20 @@ +#include + +#include +#include +#include + +#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; +}