lua_tostring
或lua_tonumber
之类的功能,但是我想使其与所输入的任何类型“兼容”。基本上,对于具有针对不同类型的变体的任何函数,我添加了它的模板版本。例如luaW_to<Foo>
https://bitbucket.org/alexames/luawrapper
// 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.
//
// In Lua, the objects are userdata, but through tricky use of metatables, they
// can be treated almost identically to tables.
//
// The main functions of interest are the following:
// luaW_is<T>
// luaW_to<T>
// luaW_check<T>
// luaW_push<T>
// luaW_register<T>
// luaW_hold<T>
// luaW_release<T>
// luaW_clean<T>
//
// These functions allow you to manipulate arbitrary classes just like you
// would the primitive types (e.g. numbers or strings). When all references
// to a userdata removed, the userdata will be deleted. In some cases, this
// may not be what you want, such as cases where an object is created in Lua,
// then passed to C++ code which owns it from then on. In these cases, you can
// call luaW_release, which releases LuaWrapper's hold on the userdata. This
// prevents it from being deallocated when all references disappear. When this
// is called, you are now responsible for calling luaW_clean manually when you
// are done with the object. Conversely, if an object is created in C++, but
// would like to pass ownership over to Lua, luaW_hold may be used.
//
// Additionally, metamethods __ctor and __dtor are provided, and will run when
// objects are created or destroyed respectively. Objects can also declare a
// list of other tables that they extend, and they will inherit all functions
// from that class.
// Todo:
// Ensure the LuaWrapper table does not collide with other tables
// Determine if it is useful to be able to call the destructor on released uds
// Add a way to transfer ownership of uds so dtor and cleanup is automatic
// Add some sort of serialization
#ifndef LUA_WRAPPER_H_
#define LUA_WRAPPER_H_
#include <iostream>
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
}
#define LUAW_BUILDER
#define luaW_getregistry(L, s) \
lua_getfield(L, LUA_REGISTRYINDEX, s)
#define luaW_setregistry(L, s) \
lua_setfield(L, LUA_REGISTRYINDEX, s)
#define LUAW_CTOR_KEY "__ctor"
#define LUAW_DTOR_KEY "__dtor"
#define LUAW_EXTENDS_KEY "__extends"
#define LUAW_STORAGE_KEY "__storage"
#define LUAW_COUNT_KEY "__counts"
#define LUAW_HOLDS_KEY "__holds"
#define LUAW_WRAPPER_KEY "LuaWrapper"
#if 0
// For Debugging
// Prints the current Lua stack, including the values for some types
template <typename T>
void luaW_printstack(lua_State* L)
{
int stack = lua_gettop(L);
for (int i = 1; i <= stack; i++)
{
std::cout << std::dec << i << ": " << lua_typename(L, lua_type(L, i));
switch(lua_type(L, i))
{
case LUA_TBOOLEAN: std::cout << " " << lua_toboolean(L, i); break;
case LUA_TSTRING: std::cout << " " << lua_tostring(L, i); break;
case LUA_TNUMBER: std::cout << " " << std::dec << (uintptr_t)lua_tointeger(L, i) << " (0x" << std::hex << lua_tointeger(L, i) << ")"; break;
default: std::cout << " " << std::hex << lua_topointer(L, i); break;
}
std::cout << std::endl;
}
}
#define LUAW_TRACE() \
printf("%s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
#define LUAW_TRACE()
#endif
template <typename T>
T* luaW_defaultallocator()
{
return new T();
}
template <typename T>
void luaW_defaultdeallocator(T* obj)
{
delete obj;
}
// This class is used with luaW_register as an alternative to using the
// normal constructor. Sometimes it's just easier to fill in the fields
// of a struct than to file in all the arguments in luaW_register,
// especially if you just want to set the last one or two.
template <typename T>
struct LuaWrapperOptions
{
LuaWrapperOptions(
const luaL_reg* table = NULL, const luaL_reg* metatable = NULL, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)
: table(table), metatable(metatable), extends(extends), disablenew(disablenew), allocator(allocator), deallocator(deallocator) { }
const luaL_reg* table;
const luaL_reg* metatable;
const char** extends;
bool disablenew;
T* (*allocator)();
void (*deallocator)(T*);
};
// 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 T* (*allocator)();
static void (*deallocator)(T*);
private:
LuaWrapper();
};
template <typename T> const char* LuaWrapper<T>::classname;
template <typename T> T* (*LuaWrapper<T>::allocator)();
template <typename T> void (*LuaWrapper<T>::deallocator)(T*);
// [-0, +0, -]
//
// 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)
{
LUAW_TRACE();
bool equal = false;
if (lua_touserdata(L, index) && lua_getmetatable(L, index))
{
// ... ud ... udmt
luaL_getmetatable(L, LuaWrapper<T>::classname); // ... ud ... udmt Tmt
equal = lua_rawequal(L, -1, -2);
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);
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;
}
// [-0, +0, -]
//
// Analogous to lua_to(boolean|string|*)
//
// Converts the given acceptable index to a T*. That value must be of type T;
// otherwise, returns NULL.
template <typename T>
T* luaW_to(lua_State* L, int index)
{
LUAW_TRACE();
T* obj = NULL;
if (luaW_is<T>(L, index))
{
obj = *(T**)lua_touserdata(L, index);
}
return obj;
}
// [-0, +0, -]
//
// Analogous to luaL_check(boolean|string|*)
//
// Checks whether the function argument at index is a T and returns this object
template <typename T>
T* luaW_check(lua_State* L, int index)
{
LUAW_TRACE();
T* obj = NULL;
if (luaW_is<T>(L, index))
{
obj = *(T**)lua_touserdata(L, index);
}
else
{
luaL_typerror(L, index, LuaWrapper<T>::classname);
}
return obj;
}
// [-0, +1, -]
//
// 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 store to it. Otherwise, a
// new store will be created for it.
template <typename T>
void luaW_push(lua_State* L, T* obj)
{
LUAW_TRACE();
T** ud = (T**)lua_newuserdata(L, sizeof(T*)); // ... obj
*ud = obj;
luaL_getmetatable(L, LuaWrapper<T>::classname); // ... obj mt
lua_setmetatable(L, -2); // ... obj
luaW_getregistry(L, LUAW_WRAPPER_KEY); // ... obj LuaWrapper
lua_getfield(L, -1, LUAW_COUNT_KEY); // ... obj LuaWrapper LuaWrapper.counts
lua_pushlightuserdata(L, obj); // ... obj LuaWrapper LuaWrapper.counts lud
lua_gettable(L, -2); // ... obj LuaWrapper LuaWrapper.counts count
int count = lua_tointeger(L, -1);
lua_pushlightuserdata(L, obj); // ... obj LuaWrapper LuaWrapper.counts count lud
lua_pushinteger(L, count+1); // ... obj LuaWrapper LuaWrapper.counts count lud count+1
lua_settable(L, -4); // ... obj LuaWrapper LuaWrapper.counts count
lua_pop(L, 3); // ... obj
}
// 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_TRACE();
luaW_getregistry(L, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.holds lud
lua_rawget(L, -2); // ... LuaWrapper LuaWrapper.holds hold
bool held = lua_toboolean(L, -1);
// If it's not held, hold it
if (!held)
{
// Apply hold boolean
lua_pop(L, 1); // ... LuaWrapper LuaWrapper.holds
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.holds lud
lua_pushboolean(L, true); // ... LuaWrapper LuaWrapper.holds lud true
lua_rawset(L, -3); // ... LuaWrapper LuaWrapper.holds
// Check count, if there's at least one, add a storage table
lua_pop(L, 1); // ... LuaWrapper
lua_getfield(L, -1, LUAW_COUNT_KEY); // ... LuaWrapper LuaWrapper.counts
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.counts lud
lua_rawget(L, -2); // ... LuaWrapper LuaWrapper.counts count
if (lua_tointeger(L, -1) > 0)
{
// Add the storage table if there isn't one already
lua_pop(L, 2);
lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.storage lud
lua_rawget(L, -2); // ... LuaWrapper LuaWrapper.storage store
if (lua_isnoneornil(L, -1))
{
lua_pop(L, 1); // ... LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.storage lud
lua_newtable(L); // ... LuaWrapper LuaWrapper.storage lud store
lua_rawset(L, -3); // ... LuaWrapper LuaWrapper.storage
lua_pop(L, 2); // ...
}
}
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.
template <typename T>
void luaW_release(lua_State* L, T* obj)
{
LUAW_TRACE();
luaW_getregistry(L, LUAW_WRAPPER_KEY); // ... LuaWrapper
lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.holds lud
lua_pushnil(L); // ... LuaWrapper LuaWrapper.counts lud nil
lua_settable(L, -3); // ... LuaWrapper LuaWrapper.counts count
lua_pop(L, 1); // ... LuaWrapper
}
// When luaW_clean is called on an object, values stored on it's Lua store
// become no longer accessible.
template <typename T>
void luaW_clean(lua_State* L, T* obj)
{
LUAW_TRACE();
lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // ... LuaWrapper LuaWrapper.storage lud
lua_pushnil(L); // ... LuaWrapper LuaWrapper.storage lud nil
lua_settable(L, -3); // ... LuaWrapper LuaWrapper.store
lua_pop(L, 2); // ...
}
// This function is called from Lua, not C++
//
// Calls the lua defined constructor ("__ctor") on a userdata. Assumes the
// userdata is on top of the stack, and numargs arguments are below it. This
// runs the CTOR_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
template <typename T>
void luaW_constructor(lua_State* L, int numargs)
{
LUAW_TRACE();
// ... ud
lua_getfield(L, -1, LUAW_CTOR_KEY); // ... ud ud.__ctor
if (lua_type(L, -1) == LUA_TFUNCTION)
{
lua_pushvalue(L, -2); // ... ud ud.__ctor ud
lua_insert(L, 1); // ud ... ud ud.__ctor
lua_insert(L, 2); // ud ud.__ctor ... ud
lua_insert(L, 3); // ud ud.__ctor ud ...
lua_call(L, numargs+1, 0); // ud
}
else
{
lua_pop(L, 1); // ... ud
}
}
// This function is generally called from Lua, not C++
//
// Creates an object of type T and calls the constructor on it with the values
// on the stack as arguments to it's constructor
template <typename T>
int luaW_new(lua_State* L)
{
LUAW_TRACE();
int numargs = lua_gettop(L);
T* obj = LuaWrapper<T>::allocator();
luaW_push<T>(L, obj);
luaW_hold<T>(L, obj);
luaW_constructor<T>(L, numargs);
return 1;
}
#ifdef LUAW_BUILDER
// This function is called from Lua, not C++
//
// This is an alternative way to construct objects. Instead of using new and a
// constructor, you can use a builder instead. A builder is called like this:
//
// f = Foo.build
// {
// X = 10;
// Y = 20;
// }
//
// This will then create a new Foo object, and then call f:X(10) and f:Y(20) on
// that object. The constructor is not called at any point. The keys in this
// table are used as function names on the metatable.
//
// This is sort of experimental, just to see if it ends up being useful.
template <typename T>
void luaW_builder(lua_State* L)
{
LUAW_TRACE();
if (lua_type(L, 1) == LUA_TTABLE)
{
// {} ud
for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1))
{
// {} ud k v
lua_pushvalue(L, -2); // {} ud k v k
lua_gettable(L, -4); // {} ud k v ud[k]
lua_pushvalue(L, -4); // {} ud k v ud[k] ud
lua_pushvalue(L, -3); // {} ud k v ud[k] ud v
lua_call(L, 2, 0); // {} ud k v
}
// {} ud
}
}
// This function is generally called from Lua, not C++
//
// Creates an object of type T and initializes it using its builder to
// initialize it
template <typename T>
int luaW_build(lua_State* L)
{
LUAW_TRACE();
T* obj = LuaWrapper<T>::allocator();
luaW_push<T>(L, obj);
luaW_hold<T>(L, obj);
luaW_builder<T>(L);
return 1;
}
#endif
// 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 fisrt 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)
{
LUAW_TRACE();
// obj key
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj key LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // obj key LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // obj key LuaWrapper LuaWrapper.table lud
lua_rawget(L, -2); // obj key LuaWrapper LuaWrapper.table table
if (!lua_isnoneornil(L, -1))
{
lua_pushvalue(L, -4); // obj key LuaWrapper LuaWrapper.table table key
lua_rawget(L, -2); // obj key LuaWrapper LuaWrapper.table table table[k]
if (lua_isnoneornil(L, -1))
{
lua_pop(L, 4); // obj key
lua_getmetatable(L, -2); // obj key mt
lua_pushvalue(L, -2); // obj key mt k
lua_rawget(L, -2); // obj key mt mt[k]
}
}
else
{
lua_pop(L, 3); // obj key
lua_getmetatable(L, -2); // obj key mt
lua_pushvalue(L, -2); // obj key mt k
lua_rawget(L, -2); // obj key mt mt[k]
}
return 1;
}
// This function is called from Lua, not C++
//
// The default metamethod to call when createing 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)
{
LUAW_TRACE();
// obj key value
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj key value LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // obj key value LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // obj key value LuaWrapper LuaWrapper.storage lud
lua_rawget(L, -2); // obj key value LuaWrapper LuaWrapper.storage store
if (!lua_isnoneornil(L, -1))
{
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key value
lua_rawset(L, -3); // obj key value LuaWrapper LuaWrapper.storage 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,
// the __dtor metamethod is called, its environment table is nil'd and pointer
// deleted.
template <typename T>
int luaW__gc(lua_State* L)
{
LUAW_TRACE();
// obj
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj LuaWrapper
lua_getfield(L, -1, LUAW_COUNT_KEY); // obj LuaWrapper LuaWrapper.counts
lua_pushlightuserdata(L, obj); // obj LuaWrapper LuaWrapper.counts lud
lua_gettable(L, -2); // obj LuaWrapper LuaWrapper.counts count
int count = lua_tointeger(L, -1);
lua_pushlightuserdata(L, obj); // obj LuaWrapper LuaWrapper.counts count lud
lua_pushinteger(L, count-1); // obj LuaWrapper LuaWrapper.counts count lud count-1
lua_settable(L, -4); // obj LuaWrapper LuaWrapper.counts count
lua_pop(L, 3); // obj LuaWrapper
if (obj && 1 == count)
{
lua_getfield(L, -1, LUAW_DTOR_KEY); // obj obj.__dtor
if (lua_type(L, -1) == LUA_TFUNCTION)
{
lua_pushvalue(L, -2); // obj obj.__ctor obj
lua_call(L, 1, 0); // obj
}
else
{
lua_pop(L, 1); // obj
}
luaW_release<T>(L, obj);
luaW_clean<T>(L, obj);
delete obj;
}
return 0;
}
// Run this to create a table and metatable for your class. You must have a
// correctly initialized LuaWrapper<T> for your class in order for this to
// properly initilize your class wrapper. This function will also take care of
// extending any classes T inherits from by copying the values in the metatable
// of the extended class to T's metatable (assuming T's metatable doesn't have
// something in that key already).
template <typename T>
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)
{
LUAW_TRACE();
LuaWrapper<T>::classname = classname;
LuaWrapper<T>::allocator = allocator;
LuaWrapper<T>::deallocator = deallocator;
const luaL_reg defaulttable[] =
{
{ "new", luaW_new<T> },
#ifdef LUAW_BUILDER
{ "build", luaW_build<T> },
#endif
{ NULL, NULL }
};
const luaL_reg defaultmetatable[] = { { "__index", luaW__index<T> }, { "__newindex", luaW__newindex<T> }, { "__gc", luaW__gc<T> }, { NULL, NULL } };
const luaL_reg emptytable[] = { { NULL, NULL } };
table = table ? table : emptytable;
metatable = metatable ? metatable : emptytable;
// Ensure that the LuaWrapper table is set up
luaW_getregistry(L, LUAW_WRAPPER_KEY); // LuaWrapper
if (lua_isnil(L, -1))
{
lua_newtable(L); // nil {}
luaW_setregistry(L, LUAW_WRAPPER_KEY); // nil
luaW_getregistry(L, LUAW_WRAPPER_KEY); // nil LuaWrapper
lua_newtable(L); // nil LuaWrapper {}
lua_setfield(L, -2, LUAW_COUNT_KEY); // nil LuaWrapper
lua_newtable(L); // nil LuaWrapper {}
lua_setfield(L, -2, LUAW_STORAGE_KEY); // nil LuaWrapper
lua_newtable(L); // nil LuaWrapper {}
lua_setfield(L, -2, LUAW_HOLDS_KEY); // nil LuaWrapper
lua_pop(L, 1); // nil
}
lua_pop(L, 1); //
// Open table
if (!disablenew)
{
luaL_register(L, LuaWrapper<T>::classname, defaulttable); // T
luaL_register(L, NULL, table); // T
}
else
{
luaL_register(L, LuaWrapper<T>::classname, table); // T
}
// Open metatable, set up extends table
luaL_newmetatable(L, LuaWrapper<T>::classname); // T mt
lua_newtable(L); // T mt {}
lua_setfield(L, -2, LUAW_EXTENDS_KEY); // T mt
luaL_register(L, NULL, defaultmetatable); // T mt
luaL_register(L, NULL, metatable); // T mt
// Copy key/value pairs from extended metatables
for (const char** e = extends; e && *e; ++e)
{
luaL_getmetatable(L, *e); // T mt emt
if(lua_isnoneornil(L, -1))
{
lua_pop(L, 1); // T mt
std::cout << "Error: did not open table " << *e << " before " << LuaWrapper<T>::classname << std::endl;
continue;
}
lua_getfield(L, -2, LUAW_EXTENDS_KEY); // T mt emt mt.__extends
lua_pushvalue(L, -2); // T mt emt mt.__extends emt
lua_setfield(L, -2, *e); // T mt emt mt.__extends
lua_getfield(L, -2, LUAW_EXTENDS_KEY); // T mt emt mt.__extends emt.__extends
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
// T mt emt mt.__extends emt.__extends k v
lua_pushvalue(L, -2); // T mt emt mt.__extends emt.__extends k v k
lua_pushvalue(L, -2); // T mt emt mt.__extends emt.__extends k v k
lua_rawset(L, -6); // T mt emt mt.__extends emt.__extends k v
}
lua_pop(L, 2); // T mt emt
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
// T mt emt k v
lua_pushvalue(L, -2); // T mt emt k v k
lua_gettable(L, -5); // T mt emt k v mt[k]
if(lua_isnoneornil(L, -1))
{
lua_pop(L, 1); // T mt emt k v
lua_pushvalue(L, -2); // T mt emt k v k
lua_pushvalue(L, -2); // T mt emt k v k v
lua_rawset(L, -6); // T mt emt k v
}
else
{
lua_pop(L, 1); // T mt k v
}
}
lua_pop(L, 1); // T mt
}
lua_setmetatable(L, -2); // T
lua_pop(L, 1); //
}
// Same as above, except sometimes its nice to be able to only have to set the
// fields you care about using a struct.
template<typename T>
void luaW_register(lua_State* L, const char* classname, LuaWrapperOptions<T>& options)
{
luaW_register(L, classname, options.table, options.metatable, options.extends, options.disablenew, options.allocator, options.deallocator);
}
#undef luaW_getregistry
#undef luaW_setregistry
#endif // LUA_WRAPPER_H_
#1 楼
我要做的第一件事就是标准化缩进:void luaW_printstack(lua_State* L)
{
int stack = lua_gettop(L);
for (int i = 1; i <= stack; i++)
{
std::cout << std::dec << i << ": " << lua_typename(L, lua_type(L, i));
switch(lua_type(L, i))
{
case LUA_TBOOLEAN: std::cout << " " << lua_toboolean(L, i); break;
case LUA_TSTRING: std::cout << " " << lua_tostring(L, i); break;
case LUA_TNUMBER: std::cout << " " << std::dec << (uintptr_t)lua_tointeger(L, i) << " (0x" << std::hex << lua_tointeger(L, i) << ")"; break;
default: std::cout << " " << std::hex << lua_topointer(L, i); break;
}
std::cout << std::endl;
}
}
为此:
void luaW_printstack(lua_State* L)
{
int stack = lua_gettop(L);
for (int i = 1; i <= stack; i++)
{
std::cout << std::dec << i << ": " << lua_typename(L, lua_type(L, i));
switch(lua_type(L, i))
{
case LUA_TBOOLEAN: std::cout << " " << lua_toboolean(L, i); break;
case LUA_TSTRING: std::cout << " " << lua_tostring(L, i); break;
case LUA_TNUMBER: std::cout << " " << std::dec << (uintptr_t)lua_tointeger(L, i) << " (0x" << std::hex << lua_tointeger(L, i) << ")"; break;
default: std::cout << " " << std::hex << lua_topointer(L, i); break;
}
std::cout << std::endl;
}
}
我认为这使跟踪变得更加容易。这使读者可以快速浏览并更快地找到块的层次结构。通常,当使用C / C99 / C ++ / C#语言,Python,PHP,Perl,Visual Basic等语言时,我们假设每一行都是一条语句。 (或者不超过一对。)当许多语句合并在一行中时,我们就会开始犯错误,并且我们的假设可能使我们误入歧途。
类似地,尽管不那么关键,
case
语句还是断行时通常可读性最强,尽管这取决于您。我的意思是这样可读性更好:
switch(lua_type(L, i))
{
case LUA_TBOOLEAN:
std::cout << " " << lua_toboolean(L, i);
break;
case LUA_TSTRING:
std::cout << " " << lua_tostring(L, i);
break;
case LUA_TNUMBER:
std::cout << " " << std::dec << (uintptr_t)lua_tointeger(L, i) << " (0x" << std::hex << lua_tointeger(L, i) << ")";
break;
default:
std::cout << " " << std::hex << lua_topointer(L, i);
break;
}
它允许读者(和可能的程序员)可以轻松地区分块,因此,每个代码段都可以彼此区分。
预处理器
if
语句非常强大,因此当我看到这样的东西:#if 0
// For Debugging
// Prints the current Lua stack, including the values for some types
template <typename T>
这让我想知道程序员的意图吗?也许,相反,您应该定义一个:
#define LUA_DEBUG
#if LUA_DEBUG
然后您可以注释掉
#define
行以更改其效果。当然,您可以稍后重用它,以使事情更容易处理。以后。
此构造函数中的以下两行:
struct LuaWrapperOptions
{
LuaWrapperOptions(
const luaL_reg* table = NULL, const luaL_reg* metatable = NULL, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)
: table(table), metatable(metatable), extends(extends), disablenew(disablenew), allocator(allocator), deallocator(deallocator) { }
const luaL_reg* table;
const luaL_reg* metatable;
const char** extends;
bool disablenew;
T* (*allocator)();
void (*deallocator)(T*);
};
很难阅读,尽管不是故意的。所有这些内联默认值都倾向于使其难以阅读。 (如果不进行深入的逗号搜索,您甚至无法分辨出有多少。)清理这些内容可以使以后的工作变得更加清晰。即使是很小的更改,例如以下帮助:
struct LuaWrapperOptions
{
LuaWrapperOptions(
const luaL_reg* table = NULL,
const luaL_reg* metatable = NULL,
const char** extends = NULL,
bool disablenew = false,
T* (*allocator)() = luaW_defaultallocator<T>,
void (*deallocator)(T*) = luaW_defaultdeallocator<T>)
: table(table), metatable(metatable), extends(extends), disablenew(disablenew), allocator(allocator), deallocator(deallocator) { }
const luaL_reg* table;
const luaL_reg* metatable;
const char** extends;
bool disablenew;
T* (*allocator)();
void (*deallocator)(T*);
};
这使读者可以轻松分辨出那里只有几个参数。但是,可以通过更详细地破坏事物来进一步改进它,如下所示:
struct LuaWrapperOptions
{
LuaWrapperOptions(
const luaL_reg* table = NULL,
const luaL_reg* metatable = NULL,
const char** extends = NULL,
bool disablenew = false,
T* (*allocator)() = luaW_defaultallocator<T>,
void (*deallocator)(T*) = luaW_defaultdeallocator<T>
)
: table(table),
metatable(metatable),
extends(extends),
disablenew(disablenew),
allocator(allocator),
deallocator(deallocator)
{ }
const luaL_reg* table;
const luaL_reg* metatable;
const char** extends;
bool disablenew;
T* (*allocator)();
void (*deallocator)(T*);
};
原始行的部分问题在于它的长度过长,即使在大屏幕上,部分代码也会被隐藏。通常,代码是最容易从上到下阅读的代码,并且在侧面产生大量噪声,从而扰乱了您的视线。另一个问题是,您无法像这样明显地区分代码两侧的错误,并且常常会忘记在常规视口中可能看不到的多余代码。
我还将最外面的代码块与其他代码分开。即:
int luaW__newindex(lua_State* L)
{
LUAW_TRACE();
// obj key value
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj key value LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // obj key value LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // obj key value LuaWrapper LuaWrapper.storage lud
lua_rawget(L, -2); // obj key value LuaWrapper LuaWrapper.storage store
if (!lua_isnoneornil(L, -1))
{
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key value
lua_rawset(L, -3); // obj key value LuaWrapper LuaWrapper.storage store
}
return 0;
}
收件人:
int luaW__newindex(lua_State* L)
{
LUAW_TRACE();
// obj key value
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj key value LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // obj key value LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // obj key value LuaWrapper LuaWrapper.storage lud
lua_rawget(L, -2); // obj key value LuaWrapper LuaWrapper.storage store
if (!lua_isnoneornil(L, -1))
{
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key value
lua_rawset(L, -3); // obj key value LuaWrapper LuaWrapper.storage store
}
return 0;
}
再次,另一个人品味,但它可以使您更轻松地区分彼此。像这样将代码直接放在
if
块上方可以给人以为它是该if
块的一部分的印象。另一条注释,如@ Hosch250所说,通常不赞成使用硬编码的返回值。 。如果您绝对需要它们,则它们应指示默认值,或者如果它们是错误,则应指示错误值。
您的注释似乎合适,您可以使用它们来跟踪那些代码不一定直接告诉您。哪个好错误的注释往往是那些过度解释代码的注释。
即:
int number = 10; // create an integer
往往是不好的,而:
int number = 10; // we have 100 of them, and 10 groups to do it, so each group is 10 objects
通常好一些。 (尽管如果代码本身可以解释这个想法,那么就不需要注释。)您的注释往往很有帮助和解释。连速记都非常有用,并会导致您按自己的方式做事。
(仅仅是示例-结果可能会有所不同。)
否则,应将这些建议与@ Hosch250的建议一起考虑。总的来说,这是写得很好的代码。
评论
\ $ \ begingroup \ $
我不确定为什么这个已有4年历史的帖子突然受到关注:P存储库中的代码比此处发布的代码新得多。关于函数luaW_printstack:该函数不再存在。隐藏在#if 0中的调试代码也没有。LuaWrapperOptions及其长的构造函数也没有使用。我不同意硬编码的返回值:在这种情况下,这是必要的。该函数是需要特定签名的回调,其中返回值是要向Lua返回多少个堆栈元素,而提到的函数始终向Lua返回0个元素。
\ $ \ endgroup \ $
– Alex Ames
15年7月14日在16:41
\ $ \ begingroup \ $
@AlexAmes-这篇文章是网站上得分最高的未回答问题之一,并提供了赏金以“清除”未回答的队列。您可以感谢janos的关注;-)。
\ $ \ endgroup \ $
–rolfl
15年7月14日在17:27
#2 楼
#define
s 这不是
#define
的好用法。应该是const string
:#define LUAW_CTOR_KEY "__ctor"
这应该是一个函数:
#define LUAW_TRACE() \
printf("%s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
硬编码的返回值:
template <typename T>
int luaW__newindex(lua_State* L)
{
LUAW_TRACE();
// obj key value
T* obj = luaW_to<T>(L, 1);
luaW_getregistry(L, LUAW_WRAPPER_KEY); // obj key value LuaWrapper
lua_getfield(L, -1, LUAW_STORAGE_KEY); // obj key value LuaWrapper LuaWrapper.storage
lua_pushlightuserdata(L, obj); // obj key value LuaWrapper LuaWrapper.storage lud
lua_rawget(L, -2); // obj key value LuaWrapper LuaWrapper.storage store
if (!lua_isnoneornil(L, -1))
{
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key
lua_pushvalue(L, -5); // obj key value LuaWrapper LuaWrapper.storage store key value
lua_rawset(L, -3); // obj key value LuaWrapper LuaWrapper.storage store
}
return 0;
}
我不是确定为什么要在那里对
0
返回值进行硬编码。它是默认索引吗?基于它是该功能中唯一的return
,它几乎看起来像是成功的信号。如果是这样,则此方法可能返回一个布尔值或成为void
。评论
\ $ \ begingroup \ $
我不确定为什么这篇文章突然引起如此多的关注。关于LUAW_TRACE(),在不要求用户将文件,行和函数作为每次调用的参数的情况下,该函数不能成为函数。宏的全部要点是神奇地填充了行号和功能。但是,无论如何,在当前版本的代码LUAW_TRACE中已被删除。关于硬编码的返回值:这是一个Lua回调函数。它需要具有它执行的功能签名,并且返回值是将堆栈顶部的多少值返回给Lua。
\ $ \ endgroup \ $
– Alex Ames
15年7月14日在16:36
评论
您能详细说明为什么它比Luabind更好吗?我不知道它是好是坏,只是有所不同。这首先是为了简化。实际上,LuaBind通过使用元编程和[]运算符来构成自己的语言。这是单个标头,不依赖boost或iostream以外的任何外部库(甚至可以取消)。它也自然适用于对类型进行操作的其余Lua函数,因此,如果您知道如何使用它们,则基本上是相同的。我使用LuaBind的次数很少,但是我怀疑这需要您多加一点力气。
LuaBridge没有依赖关系,可以执行您想做的事情。要打印堆栈,您也可以使用它。两者都是为了简单而设计的。特别是,您编写了“运行luaW_clean现在是程序员的责任”。这听起来不像简单和安全使用。
@DmitryLedentsov:这段代码已经使用了一年多了,从那以后该要求就被取消了(尽管我认为文档可能需要更新以反映这一点)。 LuaBridge看起来不错。不过,我暂时还是对LuaWrapper的项目感到满意。
什么是ud ... udmt Tmt?