Defining the methods quickly for Lunar
Lunar is a simple C++ to Lua wrapper that relies on more manual labor than other libraries like SWIG but allows for very detailed customization of what is visible to Lua. To help keep the code listing what is visible to Lua as succinct as possible I wrote several macros to clean it declarations.
The first define is for placing in the class declaration. I tend to throw this at the very top of the class definition and it defines the two things that lunar needs to create the interface.
#define LUA_CLASS_DEFINE(name)\ friend class Lua::Lunar<name>;\ friend class Lua::State\ // Lunar has been changed to use lua_class_name/lua_methods // instead of class_name/methods to make values in the debugger clearer. static const char lua_class_name[];\ static Lunar<name>::RegType lua_methods[]; The code below is to actually define the list of methods available to Lua and I usually place this at the very top of the implementation file. #define LUA_CLASS_BEGIN(name)\ const char name::lua_class_name[] = #name;\ Lunar<name>::RegType name::lua_methods[] = { #define LUA_CLASS_METHOD(name, method) { #method, &name::method } #define LUA_CLASS_END() {0,0} } and heres a quick example from the camera class LUA_CLASS_BEGIN(Camera) LUA_CLASS_METHOD(Camera, GetEye), LUA_CLASS_METHOD(Camera, SetAt), LUA_CLASS_METHOD(Camera, SetUp), LUA_CLASS_METHOD(Camera, GetEye), LUA_CLASS_METHOD(Camera, GetAt), LUA_CLASS_METHOD(Camera, GetUp), LUA_CLASS_METHOD(Camera, LockToObject), LUA_CLASS_METHOD(Camera, SwapAtAndEye), LUA_CLASS_END(); Getting data to/from Lua While coding Lua interfaces in C++ I spend a lot of time checking types and converting non-built-in types which initially led to passing lots of variables instead of a couple of more complex ones. To fix this I wrote a Wrapper for the lua_State and it's related functions. Using this wrapper I was able to change While coding Lua interfaces for C++ I found that I spent a lot of time writing input validation code to test the type safety of data passed from Lua to c++. For Box Bunny I created a set of macros to help but these only worked on basic types or became complicated and long checking large sets of data. For Deep Space I decided it would be better set up a wrapper for the lua_State* that would be able handle more complicated types transparently. Old Manual Style: int function_taking_float3_returning_float3(lua_State* L) { float3 input_val; if(lua_isnumber(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3)){ input_val.x = lua_tonumber(L, 1); input_val.x = lua_tonumber(L, 1); input_val.x = lua_tonumber(L, 1); // code using input lua_pushnumber(L, output_val.x); lua_pushnumber(L, output_val.y); lua_pushnumber(L, output_val.z); return 3; } return 0; } New Automatic Style: int function_taking_float3_returning_float3(lua_State* state) { float3 input_val; Lua::State L(state); if(L.Get(1, input_val)){ // code using input L.Push(output_val); return 1; } return 0; } Using the Lua::State functions cuts down the code for converting a float 3 from Lua to C++ and back again from 7 lines to 2, provides a nicer interface in Lua (Lua gives and receives tables containing an x, y and z variable instead of three separate variables). Interesting parts from Lua/State.h namespace Lua{ class State { public: State(std::string& working_directory); // The working directory is the directory used for all DoFile functions // Lua::State::State(lua_State*) is provided for use in function calls from Lua. // Lua::States created using Lua::State::State(lua_State*) should not exist // beyond the scope of the function call State(lua_State* state); ~State(); inline void PushNil(); inline void Push(const std::string& rhs); inline void Push(const int rhs); inline void Push(const int16_t rhs); inline void Push(const uint16_t rhs); inline void Push(const float rhs); inline void Push(const bool rhs); void Push(const float4x4& rhs); void Push(const quaternion& rhs); // Lua::State::Push(T*) is used to push values handled by Lua::Lunar to Lua template <typename T> void Push(T* const rhs); template <typename T, int N> void Push(const Math::Vector<N, T>& rhs); // For multi-part variables a return value of false does not mean that // the variable is unchanged inline bool Get(const int i, std::string& out); inline bool Get(const int i, int& out); inline bool Get(const int i, int16_t& out); inline bool Get(const int i, uint16_t& out); inline bool Get(const int i, float& out); inline bool Get(const int i, bool& out); bool Get(const int i, float4x4& out); bool Get(const int i, quaternion& rhs); template <typename T, int N> bool Get(const int i, Math::Vector<N, T>& out); // operator lua_State*() allows calling regular Lua functions that haven't been handled by // the Lua::State class instead of calling a getter. // For example: lua_istable(state, 1) instead of lua_istable(state.GetLuaState(), 1);) operator lua_State*() { return m_state; } // ASSERTs that there aren't any other references to the lua_State* and frees and recreates // a the state. void Reset(); }; } As you can see from the many inlined functions the majority of the code is very simple. For example From Lua/State.inl inline void Push(const int rhs) { lua_pushinteger(m_state, rhs); } Having these simple functions present allow the more complicated functions such as pushing a vector onto the stack. Instead of having to create a seperate function for each type and dimensionality of Math::Vector I only needed to do this: From Lua/State.inl #ifdef _MSC_VER # pragma warning(push) // Condition expression is constant (templates, if(Dim > 4)) # pragma warning(disable : 4127) #endif template <int Dim, typename T,> void State::Push(const Math::Vector<Dim, T>& rhs) { const char* var_names[] = { "x", "y", "z", "w" }; const int array_size = (Dim > 4) ? Dim : 0; const int record_size = (Dim <= 4) ? Dim : 0; // Create a table with space allocated for the data lua_createtable(m_state, array_size, record_size); for(int i = 0; i < Dim; ++i){ // For Vectors with less than 4 elements use axis name instead of numbers if(Dim > 4){ Push(i + 1); } else{ Push(var_names[i]); } // Add the element to the array Push(rhs[i]); lua_settable(m_state, -3); } } #ifdef _MSC_VER # pragma warning(pop) #endif |