├── .gitignore ├── CMakeLists.txt ├── EmbeddingLua.lua ├── README.md ├── lua-5.3.4 └── CMakeLists.txt ├── main ├── ArenaAllocator.h ├── AutomatedBinding.cpp ├── AutomatedBinding.h ├── CMakeLists.txt ├── TestRegistrations.cpp └── main.cpp └── make_VS2015.bat /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | lua-5.3.4/Makefile 3 | lua-5.3.4/README 4 | lua-5.3.4/doc/ 5 | lua-5.3.4/src/ 6 | /rttr-0.9.6-src 7 | /rttr-0.9.6-src.zip 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project( EmbeddingLua ) 4 | 5 | # Turn on the ability to create folders to organize projects (.vcproj) 6 | # It creates "CMakePredefinedTargets" folder by default and adds CMake 7 | # defined projects like INSTALL.vcproj and ZERO_CHECK.vcproj 8 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 9 | 10 | # Sub-directories where more CMakeLists.txt exist 11 | add_subdirectory(main) 12 | add_subdirectory(lua-5.3.4) 13 | -------------------------------------------------------------------------------- /EmbeddingLua.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | - How to use the Lua C API 3 | - Bind Native C++ Object and use them in Lua 4 | - Assume you don't know anything about the Lua C API 5 | - Learning by Doing 6 | - Code is all in github https://github.com/davepoo/EmbeddingLuaTutorial 7 | ]] 8 | --[[ 9 | - You should know 10 | - How to script in Lua (although you don't need to be an expert at all) 11 | - How to code in C++ / C++11 12 | ]] 13 | 14 | --[[ 15 | - Going to use 16 | - Lua 5.3.4, 17 | - Windows, 18 | - CMake & Visual Studio 2015 Community 19 | - You can use CMake to build in any way you want (VS2017, XCode...) 20 | ]] 21 | 22 | --[[ 23 | == Contents == 24 | - download lua and use cmake to make a lua application and compile it 25 | - create lua state and execute a lua code inline 26 | - explaination of the types in lua 27 | - explaination of the lua stack 28 | - get a global variable from lua 29 | - set a global variable to lua 30 | - call a lua function 31 | - call a lua function and get the result(s) 32 | - call a lua function with parameters and get the result(s) 33 | - call a c function from lua 34 | - call a c function from lua with a return value 35 | - call a c function from lua with a return value and parameters 36 | - creating your own type for lua, userdata 37 | - Creating C++ objects in lua 38 | - Calling C++ class methods in lua 39 | - using tables, getting settings values from a table in lua 40 | - setting values in a table in lua 41 | - giving data to lua via globals 42 | - metatables 43 | - metatables on userdata 44 | - uservalues 45 | - memory allocation in lua 46 | - the registry 47 | - calling C functions with upvalues 48 | - ?? lightuserdata 49 | - Runtime type information 50 | - Generic Binding of C++/C to Lua 51 | ]]-- -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Embedding Lua Tutorial 2 | 3 | Files to accompany tutorial videos on how to embed Lua in a native application. 4 | 5 | The playlist for video tutorials are at https://youtu.be/xrLQ0OXfjaI 6 | 7 | ### How To Build The Project 8 | * This project requires RTTR library from http://www.rttr.org, see https://youtu.be/O0f7aGU61II for the installation of that library into the tutorial. 9 | * Download & place the downloaded lua source files in the lua-5.3.4 folder 10 | * run make_VS2015.bat to create the Visual Studio Project 11 | * build/ folder should be created, open the EmbeddingLua.sln 12 | 13 | ### Main Source File 14 | main/main.cpp is the source for the application that lua is being embedded. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lua-5.3.4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project( LuaLib ) 4 | 5 | # The recommended way to collect sources in variable by explicitly specifying the source files 6 | set (LUA_RUNTIME_SOURCES 7 | "src/lapi.c" 8 | "src/lapi.h" 9 | "src/lauxlib.c" 10 | "src/lauxlib.h" 11 | "src/lbaselib.c" 12 | "src/lbitlib.c" 13 | "src/lcode.c" 14 | "src/lcode.h" 15 | "src/lcorolib.c" 16 | "src/lctype.c" 17 | "src/lctype.h" 18 | "src/ldblib.c" 19 | "src/ldebug.c" 20 | "src/ldebug.h" 21 | "src/ldo.c" 22 | "src/ldo.h" 23 | "src/ldump.c" 24 | "src/lfunc.c" 25 | "src/lfunc.h" 26 | "src/lgc.c" 27 | "src/lgc.h" 28 | "src/linit.c" 29 | "src/liolib.c" 30 | "src/llex.c" 31 | "src/llex.h" 32 | "src/llimits.h" 33 | "src/lmathlib.c" 34 | "src/lmem.c" 35 | "src/lmem.h" 36 | "src/loadlib.c" 37 | "src/lobject.c" 38 | "src/lobject.h" 39 | "src/lopcodes.c" 40 | "src/lopcodes.h" 41 | "src/loslib.c" 42 | "src/lparser.c" 43 | "src/lparser.h" 44 | "src/lprefix.h" 45 | "src/lstate.c" 46 | "src/lstate.h" 47 | "src/lstring.c" 48 | "src/lstring.h" 49 | "src/lstrlib.c" 50 | "src/ltable.c" 51 | "src/ltable.h" 52 | "src/ltablib.c" 53 | "src/ltm.c" 54 | "src/ltm.h" 55 | #"src/lua.c" 56 | #"src/lua.h" 57 | "src/lua.hpp" 58 | #"src/luac.c" 59 | "src/luaconf.h" 60 | "src/lualib.h" 61 | "src/lundump.c" 62 | "src/lundump.h" 63 | "src/lutf8lib.c" 64 | "src/lvm.c" 65 | "src/lvm.h" 66 | "src/lzio.c" 67 | "src/lzio.h") 68 | 69 | add_library( LuaLib ${LUA_RUNTIME_SOURCES} ) 70 | 71 | target_include_directories ( LuaLib PUBLIC "${PROJECT_SOURCE_DIR}/src") 72 | 73 | -------------------------------------------------------------------------------- /main/ArenaAllocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | /*! \brief Allocates from global memory (NOTE: does not currently align memory) */ 7 | struct GlobalAllocator 8 | { 9 | void* Allocate(size_t sizeBytes) 10 | { 11 | return ::operator new(sizeBytes); 12 | } 13 | 14 | void DeAllocate(void* ptr, size_t /*osize*/) 15 | { 16 | assert(ptr != nullptr); //can't decallocate null!!! 17 | ::operator delete(ptr); 18 | } 19 | 20 | void* ReAllocate(void* ptr, size_t osize, size_t nsize) 21 | { 22 | size_t bytesToCopy = osize; 23 | if (nsize < bytesToCopy) 24 | { 25 | bytesToCopy = nsize; 26 | } 27 | void* newPtr = Allocate(nsize); 28 | memcpy(newPtr, ptr, bytesToCopy); 29 | DeAllocate(ptr, osize); 30 | return newPtr; 31 | } 32 | 33 | static void *l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { 34 | GlobalAllocator * pool = static_cast(ud); 35 | if (nsize == 0) 36 | { 37 | if (ptr != nullptr) 38 | { 39 | pool->DeAllocate(ptr, osize); 40 | } 41 | return NULL; 42 | } 43 | else 44 | { 45 | if (ptr == nullptr) 46 | { 47 | return pool->Allocate(nsize); 48 | } 49 | else 50 | { 51 | return pool->ReAllocate(ptr, osize, nsize); 52 | } 53 | } 54 | } 55 | }; 56 | 57 | /*! \brief Allocates from a fixed pool. 58 | * Aligns all memory to 8 bytes 59 | * Has a min allocation of 64 bytes 60 | * Puts all free'd blocks on a free list. 61 | * Falls back to GlobalAllocator when out of memory. */ 62 | struct ArenaAllocator 63 | { 64 | void* m_begin; 65 | void* m_end; 66 | char* m_curr; 67 | 68 | static constexpr int ALIGNMENT = 8; 69 | static constexpr int MIN_BLOCK_SIZE = ALIGNMENT * 8; 70 | 71 | struct FreeList 72 | { 73 | FreeList* m_next; 74 | }; 75 | 76 | FreeList* m_freeListHead; 77 | GlobalAllocator m_globalAllocator; 78 | 79 | ArenaAllocator(void* begin, void* end) : 80 | m_begin(begin), 81 | m_end(end) 82 | { 83 | Reset(); 84 | } 85 | 86 | void Reset() 87 | { 88 | m_freeListHead = nullptr; 89 | m_curr = static_cast(m_begin); 90 | } 91 | 92 | size_t SizeToAllocate(size_t size) 93 | { 94 | size_t allocatedSize = size; 95 | if (allocatedSize < MIN_BLOCK_SIZE) 96 | { 97 | allocatedSize = MIN_BLOCK_SIZE; 98 | } 99 | return allocatedSize; 100 | } 101 | 102 | void* Allocate(size_t sizeBytes) 103 | { 104 | if ( sizeBytes <= MIN_BLOCK_SIZE && m_freeListHead) 105 | { 106 | //printf("-- allocated from the freelist --\n"); 107 | void* ptr = m_freeListHead; 108 | m_freeListHead = m_freeListHead->m_next; 109 | return ptr; 110 | } 111 | else 112 | { 113 | size_t allocatedBytes = SizeToAllocate( sizeBytes ); 114 | m_curr = (char*)((uintptr_t)m_curr + (ALIGNMENT - 1) & ~(ALIGNMENT - 1)); 115 | if (m_curr + allocatedBytes <= m_end) 116 | { 117 | //printf("Allocated %d bytes\n", (int)allocatedBytes); 118 | void* ptr = m_curr; 119 | m_curr += allocatedBytes; 120 | return ptr; 121 | } 122 | else 123 | { 124 | return m_globalAllocator.Allocate(sizeBytes); 125 | } 126 | } 127 | } 128 | 129 | void DeAllocate(void* ptr, size_t osize) 130 | { 131 | assert(ptr != nullptr); //can't decallocate null!!! 132 | if (ptr >= m_begin && ptr <= m_end) 133 | { 134 | size_t allocatedBytes = SizeToAllocate(osize); 135 | //printf("DeAllocated %d bytes\n", (int)allocatedBytes); 136 | assert( allocatedBytes >= MIN_BLOCK_SIZE ); 137 | //printf("-- deallocated to the freelist --\n"); 138 | FreeList* newHead = static_cast(ptr); 139 | newHead->m_next = m_freeListHead; 140 | m_freeListHead = newHead; 141 | 142 | } 143 | else 144 | { 145 | m_globalAllocator.DeAllocate(ptr, osize); 146 | } 147 | } 148 | 149 | void* ReAllocate(void* ptr, size_t osize, size_t nsize) 150 | { 151 | //printf("ReAllocated %d bytes\n", (int)nsize); 152 | size_t bytesToCopy = osize; 153 | if (nsize < bytesToCopy) 154 | { 155 | bytesToCopy = nsize; 156 | } 157 | void* newPtr = Allocate(nsize); 158 | memcpy(newPtr, ptr, bytesToCopy); 159 | DeAllocate(ptr, osize); 160 | return newPtr; 161 | } 162 | 163 | static void *l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { 164 | ArenaAllocator * pool = static_cast(ud); 165 | if (nsize == 0) 166 | { 167 | if (ptr != nullptr) 168 | { 169 | pool->DeAllocate(ptr, osize); 170 | } 171 | return NULL; 172 | } 173 | else 174 | { 175 | if (ptr == nullptr) 176 | { 177 | return pool->Allocate(nsize); 178 | } 179 | else 180 | { 181 | return pool->ReAllocate(ptr, osize, nsize); 182 | } 183 | } 184 | } 185 | }; -------------------------------------------------------------------------------- /main/AutomatedBinding.cpp: -------------------------------------------------------------------------------- 1 | #include "AutomatedBinding.h" 2 | #include "ArenaAllocator.h" 3 | #include 4 | #include 5 | 6 | int CreateUserDatumFromVariant( lua_State* L, const rttr::variant& v ); 7 | 8 | int ToLua( lua_State* L, rttr::variant& result ) 9 | { 10 | int numberOfReturnValues = 0; 11 | if (result.is_valid() == false) 12 | { 13 | luaL_error(L, "unable to send to Lua type '%s'\n", result.get_type().get_name().to_string().c_str()); 14 | } 15 | else if (result.is_type() == false) 16 | { 17 | if (result.is_type()) 18 | { 19 | lua_pushnumber(L, result.get_value()); 20 | numberOfReturnValues++; 21 | } 22 | else if (result.is_type()) 23 | { 24 | lua_pushnumber(L, result.get_value()); 25 | numberOfReturnValues++; 26 | } 27 | else if ( result.get_type().is_class() || result.get_type().is_pointer() ) 28 | { 29 | numberOfReturnValues += CreateUserDatumFromVariant( L, result ); 30 | } 31 | else 32 | { 33 | luaL_error(L, 34 | "unhandled type '%s' being sent to Lua.\n", 35 | result.get_type().get_name().to_string().c_str()); 36 | } 37 | } 38 | return numberOfReturnValues; 39 | } 40 | 41 | /*! \brief Invoke #methodToInvoke on #object, passing the arguments to the method from Lua and leave the result on the Lua stack. 42 | * - Assumes that the top of the stack downwards is filled with the parameters to the method we are invoking. 43 | * - To call a free function pass rttr::instance = {} as #object 44 | * \return the number of values left on the Lua stack */ 45 | int InvokeMethod( lua_State* L, rttr::method& methodToInvoke, rttr::instance& object ) 46 | { 47 | rttr::array_range nativeParams = methodToInvoke.get_parameter_infos(); 48 | int luaParamsStackOffset = 0; 49 | int numNativeArgs = (int)nativeParams.size(); 50 | int numLuaArgs = lua_gettop(L); 51 | if (numLuaArgs > numNativeArgs) 52 | { 53 | luaParamsStackOffset = numLuaArgs - numNativeArgs; 54 | numLuaArgs = numNativeArgs; 55 | } 56 | if (numLuaArgs != numNativeArgs) 57 | { 58 | printf("Error calling native function '%s', wrong number of args, expected %d, got %d\n", 59 | methodToInvoke.get_name().to_string().c_str(), numNativeArgs, numLuaArgs); 60 | assert(numLuaArgs == numNativeArgs); 61 | } 62 | union PassByValue 63 | { 64 | int intVal; 65 | short shortVal; 66 | }; 67 | 68 | std::vector pbv(numNativeArgs); 69 | std::vector nativeArgs(numNativeArgs); 70 | auto nativeParamsIt = nativeParams.begin(); 71 | for (int i = 0; i < numLuaArgs; i++, nativeParamsIt++) 72 | { 73 | const rttr::type nativeParamType = nativeParamsIt->get_type(); 74 | int luaArgIdx = i + 1 + luaParamsStackOffset; 75 | int luaType = lua_type(L, luaArgIdx); 76 | switch (luaType) 77 | { 78 | case LUA_TNUMBER: 79 | if (nativeParamType == rttr::type::get()) 80 | { 81 | pbv[i].intVal = (int)lua_tonumber(L, luaArgIdx); 82 | nativeArgs[i] = pbv[i].intVal; 83 | } 84 | else if (nativeParamType == rttr::type::get()) 85 | { 86 | pbv[i].shortVal = (short)lua_tonumber(L, luaArgIdx); 87 | nativeArgs[i] = pbv[i].shortVal; 88 | } 89 | else 90 | { 91 | printf("unrecognised parameter type '%s'\n", nativeParamType.get_name().to_string().c_str()); 92 | assert(false); 93 | } 94 | break; 95 | default: 96 | luaL_error(L, "Don't know this lua type '%s', parameter %d when calling '%s'", 97 | lua_typename(L, luaType), 98 | i, 99 | methodToInvoke.get_name().to_string().c_str()); 100 | break; 101 | } 102 | } 103 | rttr::variant result = methodToInvoke.invoke_variadic(object, nativeArgs); 104 | return ToLua(L, result); 105 | } 106 | 107 | int CallGlobalFromLua(lua_State* L) 108 | { 109 | rttr::method* m = (rttr::method*)lua_touserdata(L, lua_upvalueindex(1)); 110 | rttr::method& methodToInvoke(*m); 111 | rttr::instance object = {}; 112 | return InvokeMethod(L, methodToInvoke, object); 113 | } 114 | 115 | /*! \return The meta table name for type t */ 116 | std::string MetaTableName( const rttr::type& t ) 117 | { 118 | std::string metaTableName; 119 | if ( t.is_pointer() ) 120 | { 121 | metaTableName = t.get_raw_type().get_name().to_string(); 122 | } 123 | else 124 | { 125 | metaTableName = t.get_name().to_string(); 126 | } 127 | metaTableName.append("_MT_"); 128 | return metaTableName; 129 | } 130 | 131 | int CreateUserDatumFromVariant( lua_State* L, const rttr::variant& v ) 132 | { 133 | void* ud = lua_newuserdata( L, sizeof( rttr::variant ) ); 134 | int userDatumStackIndex = lua_gettop( L ); 135 | new (ud) rttr::variant( v ); 136 | 137 | luaL_getmetatable( L, MetaTableName( v.get_type() ).c_str() ); 138 | lua_setmetatable( L, userDatumStackIndex ); 139 | 140 | lua_newtable( L ); 141 | lua_setuservalue( L, userDatumStackIndex ); 142 | 143 | return 1; //return the userdatum 144 | } 145 | 146 | int CreateUserDatum(lua_State* L) 147 | { 148 | const char* typeName = (const char*)lua_tostring(L, lua_upvalueindex(1)); 149 | rttr::type typeToCreate = rttr::type::get_by_name(typeName); 150 | 151 | void* ud = lua_newuserdata(L, sizeof(rttr::variant) ); 152 | new (ud) rttr::variant(typeToCreate.create()); 153 | //rttr::variant& variant = *(rttr::variant*)ud; 154 | 155 | luaL_getmetatable(L, MetaTableName(typeToCreate).c_str()); 156 | lua_setmetatable(L, 1); 157 | 158 | lua_newtable(L); 159 | lua_setuservalue(L, 1); 160 | 161 | return 1; //return the userdatum 162 | } 163 | 164 | int DestroyUserDatum(lua_State* L) 165 | { 166 | rttr::variant* ud = (rttr::variant*)lua_touserdata(L, -1); 167 | ud->~variant(); 168 | return 0; 169 | } 170 | 171 | int InvokeFuncOnUserDatum(lua_State* L) 172 | { 173 | rttr::method& m = *(rttr::method*)lua_touserdata(L, lua_upvalueindex(1)); 174 | if (lua_isuserdata(L, 1) == false) 175 | { 176 | luaL_error(L, "Expected a userdatum on the lua stack when invoking native method '%s'", m.get_name().to_string().c_str()); 177 | } 178 | 179 | rttr::variant& ud = *(rttr::variant*)lua_touserdata(L, 1); 180 | rttr::instance object(ud); 181 | return InvokeMethod(L, m, object); 182 | } 183 | 184 | int IndexUserDatum(lua_State* L) 185 | { 186 | const char* typeName = (const char*)lua_tostring(L, lua_upvalueindex(1)); 187 | rttr::type typeInfo = rttr::type::get_by_name(typeName); 188 | if (lua_isuserdata(L, 1) == false) 189 | { 190 | luaL_error(L, "Expected a userdatum on the lua stack when indexing native type '%s'", typeName); 191 | } 192 | 193 | if (lua_isstring(L, 2) == false) 194 | { 195 | luaL_error(L, "Expected a name of a native property or method when indexing native type '%s'", typeName); 196 | } 197 | 198 | const char* fieldName = lua_tostring(L, 2); 199 | rttr::method m = typeInfo.get_method(fieldName); 200 | if (m.is_valid()) 201 | { 202 | void* methodUD = lua_newuserdata(L, sizeof(rttr::method)); 203 | new (methodUD) rttr::method(m); 204 | lua_pushcclosure(L, InvokeFuncOnUserDatum, 1); 205 | return 1; 206 | } 207 | 208 | rttr::property p = typeInfo.get_property(fieldName); 209 | if (p.is_valid()) 210 | { 211 | rttr::variant& ud = *(rttr::variant*)lua_touserdata(L, 1); 212 | rttr::variant result = p.get_value(ud); 213 | if (result.is_valid()) 214 | { 215 | return ToLua(L, result); 216 | } 217 | } 218 | 219 | //if it's not a method or property then return the uservalue 220 | lua_getuservalue(L, 1); 221 | lua_pushvalue(L, 2); 222 | lua_gettable(L, -2); 223 | return 1; 224 | } 225 | 226 | int NewIndexUserDatum(lua_State* L) 227 | { 228 | const char* typeName = (const char*)lua_tostring(L, lua_upvalueindex(1)); 229 | rttr::type typeInfo = rttr::type::get_by_name(typeName); 230 | if (lua_isuserdata(L, 1) == false) 231 | { 232 | luaL_error(L, "Expected a userdatum on the lua stack when indexing native type '%s'", typeName); 233 | } 234 | 235 | if (lua_isstring(L, 2) == false) 236 | { 237 | luaL_error(L, "Expected a name of a native property or method when indexing native type '%s'", typeName); 238 | } 239 | 240 | // 3 - the value we are writing to the object 241 | 242 | const char* fieldName = lua_tostring(L, 2); 243 | rttr::property p = typeInfo.get_property(fieldName); 244 | if (p.is_valid()) 245 | { 246 | rttr::variant& ud = *(rttr::variant*)lua_touserdata(L, 1); 247 | int luaType = lua_type(L, 3); 248 | switch (luaType) 249 | { 250 | case LUA_TNUMBER: 251 | if (p.get_type() == rttr::type::get()) 252 | { 253 | int val = (int)lua_tonumber(L, 3); 254 | assert( p.set_value(ud, val) ); 255 | } 256 | else if (p.get_type() == rttr::type::get()) 257 | { 258 | short val = (short)lua_tonumber(L, 3); 259 | assert(p.set_value(ud, val)); 260 | } 261 | else 262 | { 263 | luaL_error(L, 264 | "Cannot set the value '%s' on this type '%s', we didn't recognise the native type '%s'", 265 | fieldName, typeName, p.get_type().get_name().to_string().c_str() ); 266 | } 267 | break; 268 | default: 269 | luaL_error(L, 270 | "Cannot set the value '%s' on this type '%s', we didnt recognise the lua type '%s'", 271 | fieldName, typeName, lua_typename(L, luaType) ); 272 | break; 273 | } 274 | 275 | return 0; 276 | } 277 | 278 | //if it wasn't a property then set it as a uservalue 279 | lua_getuservalue(L, 1); 280 | lua_pushvalue(L, 2); 281 | lua_pushvalue(L, 3); 282 | lua_settable(L, -3); 283 | return 0; 284 | } 285 | 286 | lua_State* CreateScript( ArenaAllocator& pool ) 287 | { 288 | //open the Lua state using our memory pool 289 | lua_State* L = lua_newstate( ArenaAllocator::l_alloc, &pool ); 290 | 291 | lua_newtable( L ); 292 | lua_pushvalue( L, -1 ); 293 | lua_setglobal( L, "Global" ); 294 | 295 | //binding global methods 296 | lua_pushvalue( L, -1 ); //1 297 | for ( auto& method : rttr::type::get_global_methods() ) 298 | { 299 | lua_pushstring( L, method.get_name().to_string().c_str() ); //2 300 | lua_pushlightuserdata( L, ( void* )&method ); 301 | lua_pushcclosure( L, CallGlobalFromLua, 1 ); //3 302 | lua_settable( L, -3 ); //1[2] = 3 303 | } 304 | 305 | //binding classes to Lua 306 | for ( auto& classToRegister : rttr::type::get_types() ) 307 | { 308 | if ( classToRegister.is_class() ) 309 | { 310 | const std::string s = classToRegister.get_name().to_string(); 311 | const char* typeName = s.c_str(); 312 | 313 | lua_newtable( L ); 314 | lua_pushvalue( L, -1 ); 315 | lua_setglobal( L, classToRegister.get_name().to_string().c_str() ); 316 | 317 | lua_pushvalue( L, -1 ); 318 | lua_pushstring( L, typeName ); 319 | lua_pushcclosure( L, CreateUserDatum, 1 ); 320 | lua_setfield( L, -2, "new" ); 321 | 322 | //create the metatable & metamethods for this type 323 | luaL_newmetatable( L, MetaTableName( classToRegister ).c_str() ); 324 | lua_pushstring( L, "__gc" ); 325 | lua_pushcfunction( L, DestroyUserDatum ); 326 | lua_settable( L, -3 ); 327 | 328 | lua_pushstring( L, "__index" ); 329 | lua_pushstring( L, typeName ); 330 | lua_pushcclosure( L, IndexUserDatum, 1 ); 331 | lua_settable( L, -3 ); 332 | 333 | lua_pushstring( L, "__newindex" ); 334 | lua_pushstring( L, typeName ); 335 | lua_pushcclosure( L, NewIndexUserDatum, 1 ); 336 | lua_settable( L, -3 ); 337 | } 338 | } 339 | 340 | return L; 341 | } 342 | 343 | int LoadScript( lua_State* L, const char* script ) 344 | { 345 | return luaL_loadstring( L, script ); 346 | } 347 | 348 | int ExecuteScript( lua_State* L ) 349 | { 350 | return lua_pcall( L, 0, LUA_MULTRET, 0 ); 351 | } 352 | 353 | void CloseScript( lua_State* L ) 354 | { 355 | lua_close( L ); 356 | } -------------------------------------------------------------------------------- /main/AutomatedBinding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "lua.hpp" 3 | #include 4 | 5 | struct ArenaAllocator; 6 | 7 | lua_State* CreateScript( ArenaAllocator& pool ); 8 | int LoadScript( lua_State* L, const char* script ); 9 | int ExecuteScript( lua_State* L ); 10 | void CloseScript( lua_State* L ); 11 | 12 | /*! \brief Takes the result and puts it onto the Lua stack 13 | * \return the number of values left on the stack. */ 14 | int ToLua( lua_State* L, rttr::variant& result ); 15 | 16 | inline int PutOnLuaStack( lua_State* ) 17 | { 18 | return 0; 19 | } 20 | 21 | template< typename T > 22 | inline int PutOnLuaStack( lua_State* L, T& toPutOnStack ) 23 | { 24 | rttr::type typeOfT = rttr::type::get(); 25 | if ( typeOfT.is_class() ) 26 | { 27 | //pass-by-reference 28 | rttr::variant v( &toPutOnStack ); 29 | return ToLua( L, v ); 30 | } 31 | else 32 | { 33 | //pass-by-value 34 | rttr::variant v( toPutOnStack ); 35 | return ToLua( L, v ); 36 | } 37 | } 38 | 39 | template< typename T, typename... T2 > 40 | inline int PutOnLuaStack( lua_State* L, T& toPutOnStack, T2&... moreArgs ) 41 | { 42 | return PutOnLuaStack( L, toPutOnStack ) + PutOnLuaStack( L, moreArgs... ); 43 | } 44 | 45 | template< typename... ARGS > 46 | inline void CallScriptFunction( lua_State* L, const char* funcName, ARGS&... args ) 47 | { 48 | lua_getglobal( L, funcName ); 49 | if ( lua_type( L, -1 ) == LUA_TFUNCTION ) 50 | { 51 | int numArgs = PutOnLuaStack( L, args... ); 52 | if ( lua_pcall( L, numArgs, 0, 0 ) != 0 ) 53 | { 54 | printf( "unable to call script function '%s', '%s'\n", funcName, lua_tostring( L, -1 ) ); 55 | luaL_error( L, "unable to call script function '%s', '%s'", funcName, lua_tostring( L, -1 ) ); 56 | } 57 | } 58 | else 59 | { 60 | printf( "unknown script function '%s'\n", funcName ); 61 | luaL_error( L, "unknown script function '%s'", funcName ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project( LuaTutorial ) 4 | 5 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLUA_TUTORIAL_DEBUG" ) #so we can add the LUA_TUTORIAL_DEBUG preprocessor define and other flags to stay in debug mode - see https://cmake.org/Wiki/CMake_Useful_Variables#Compilers_and_Tools 6 | set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLUA_TUTORIAL_DEBUG" ) 7 | 8 | if(MSVC) 9 | add_compile_options(/MP) #Use multiple processors when building 10 | add_compile_options(/W4 /wd4201 /WX) #Warning level 4, all warnings are errors 11 | else() 12 | add_compile_options(-W -Wall -Werror) #All Warnings, all warnings are errors 13 | endif() 14 | 15 | # source for the test executable 16 | set (LUA_TUTORIAL_SOURCES 17 | "main.cpp" 18 | "ArenaAllocator.h" 19 | "AutomatedBinding.h" 20 | "AutomatedBinding.cpp" 21 | "TestRegistrations.cpp" ) 22 | 23 | source_group("src" FILES ${LUA_TUTORIAL_SOURCES}) 24 | 25 | add_executable( LuaTutorial 26 | ${LUA_TUTORIAL_SOURCES} 27 | ) 28 | 29 | target_link_libraries( LuaTutorial PUBLIC LuaLib ) 30 | 31 | find_package(RTTR CONFIG REQUIRED Core) 32 | target_link_libraries(LuaTutorial PUBLIC RTTR::Core_Lib) # rttr as static library -------------------------------------------------------------------------------- /main/TestRegistrations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ArenaAllocator.h" 4 | #include "AutomatedBinding.h" 5 | 6 | // This Cpp file contains the stuff we are going to 7 | //Register with RTTR and will be bound to Lua. 8 | //NOTE: this file nor any of the contents does not need to 9 | //be directly included/referenced in our Lua bindings. 10 | 11 | void HelloWorld() 12 | { 13 | printf("Hello, World\n"); 14 | } 15 | 16 | void HelloWorld2() 17 | { 18 | printf("Hello, World 2\n"); 19 | } 20 | 21 | void HelloWorld3( int x, short y, int z ) 22 | { 23 | printf("Hello, World 3 (%d, %d, %d)\n", x, y, z); 24 | } 25 | 26 | short Add(short a, short b) 27 | { 28 | return a + b; 29 | } 30 | 31 | short Mul(short a, short b) 32 | { 33 | return a * b; 34 | } 35 | 36 | struct Sprite 37 | { 38 | int x; 39 | int y; 40 | 41 | Sprite() : x(0), y(0) {} 42 | ~Sprite() {} 43 | 44 | int Move(int velX, int velY) 45 | { 46 | x += velX; 47 | y += velY; 48 | return x + y; 49 | } 50 | 51 | void Draw() 52 | { 53 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 54 | } 55 | }; 56 | 57 | RTTR_REGISTRATION 58 | { 59 | rttr::registration::method("HelloWorld", &HelloWorld); 60 | rttr::registration::method("HelloWorld2", &HelloWorld2); 61 | rttr::registration::method("HelloWorld3", &HelloWorld3); 62 | rttr::registration::method("Add", &Add); 63 | rttr::registration::method("Mul", &Mul); 64 | rttr::registration::class_("Sprite") 65 | .constructor() 66 | .method("Move", &Sprite::Move) 67 | .method("Draw", &Sprite::Draw) 68 | .property("x", &Sprite::x) 69 | .property("y", &Sprite::y); 70 | } 71 | 72 | /*! \brief The Lua script, you would probably load this data from a .lua file. */ 73 | constexpr char* LUA_SCRIPT = R"( 74 | -- this is a lua script 75 | Global.HelloWorld() 76 | Global.HelloWorld2() 77 | local c = Global.Add( 42, 43 ) 78 | local d = Global.Mul( c, 2 ) 79 | Global.HelloWorld3( d, 99, 111 ) 80 | local spr = Sprite.new() 81 | local xplusy = spr:Move( 1, 2 ) 82 | local x = spr.x 83 | spr:Move( xplusy + x, xplusy ) 84 | spr:Draw() 85 | spr.zzzz = 42 86 | local z = spr.zzzz 87 | spr:Move( z, z ) 88 | spr:Draw() 89 | spr.x = 10 90 | spr:Draw() 91 | 92 | function Foo3( x, y, z ) 93 | Global.HelloWorld3( x, y, z ) 94 | end 95 | 96 | function Foo2( x, y ) 97 | Global.HelloWorld3( x, y, y ) 98 | end 99 | 100 | function Foo1( x ) 101 | Global.HelloWorld3( x, x, x ) 102 | end 103 | 104 | function Foo( ) 105 | Global.HelloWorld3( 42, 44, 43 ) 106 | end 107 | 108 | function Render( sprite ) 109 | sprite.x = sprite.x + 10 110 | sprite:Draw() 111 | end 112 | 113 | )"; 114 | 115 | /*! \brief This is our test application for lua binding with RTTR */ 116 | void AutomatedBindingTutorial() 117 | { 118 | printf( "---- automated binding using run time type info -----\n" ); 119 | 120 | //create memory pool for Lua 121 | constexpr int POOL_SIZE = 1024 * 20; 122 | char memory[POOL_SIZE]; 123 | ArenaAllocator pool( memory, &memory[POOL_SIZE - 1] ); 124 | 125 | //Create our Lua Script 126 | lua_State* L = CreateScript( pool ); 127 | 128 | //load & execute the lua script 129 | LoadScript( L, LUA_SCRIPT ); 130 | if ( ExecuteScript( L ) != LUA_OK ) 131 | { 132 | printf( "Error: %s\n", lua_tostring( L, -1 ) ); 133 | } 134 | 135 | //call our script functions 136 | int one = 1; 137 | int two = 2; 138 | int three = 3; 139 | CallScriptFunction( L, "Foo3", one, two, three ); 140 | CallScriptFunction( L, "Foo2", one, two ); 141 | CallScriptFunction( L, "Foo1", one ); 142 | CallScriptFunction( L, "Foo" ); 143 | 144 | Sprite sprite; 145 | sprite.x = 100; 146 | CallScriptFunction( L, "Render", sprite ); 147 | CallScriptFunction( L, "Render", sprite ); 148 | 149 | //close the Lua state 150 | CloseScript( L ); 151 | } -------------------------------------------------------------------------------- /main/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ArenaAllocator.h" 2 | #include 3 | #include "lua.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "AutomatedBinding.h" 9 | 10 | int main() 11 | { 12 | { 13 | lua_State* L = luaL_newstate(); 14 | luaL_dostring(L, "x = 47"); 15 | lua_getglobal(L, "x"); 16 | lua_Number x = lua_tonumber(L, 1); 17 | printf("lua says x = %d\n", (int)x); 18 | lua_close(L); 19 | } 20 | 21 | // nil 22 | // boolean 23 | // light userdata 24 | // number 25 | // string 26 | // table x = { "foo", "bar" } 27 | // function 28 | // userdata 29 | // thread 30 | 31 | { 32 | lua_State* L = luaL_newstate(); 33 | lua_pushnumber(L, 42); 34 | lua_pushnumber(L, 52); 35 | lua_pushnumber(L, 62); 36 | 37 | // 42 - 1 38 | // 52 - 2 39 | // 62 - 3 40 | 41 | lua_Number x = lua_tonumber(L, 3); 42 | printf("lua says x = %d\n", (int)x); 43 | 44 | // 42 - -3 45 | // 52 - -2 46 | // 62 - -1 47 | 48 | lua_Number xx = lua_tonumber(L, -1); 49 | printf("the last thing i pushed was %d\n", (int)xx); 50 | 51 | lua_remove(L, 2); //this should remove 52 from the stack 52 | 53 | // 42 - -2 54 | // 62 - -1 55 | 56 | // 42 - 1 57 | // 62 - 2 58 | 59 | lua_Number xxx = lua_tonumber(L, 2); 60 | printf("position 2 is %d\n", (int)xxx); 61 | 62 | lua_close(L); 63 | } 64 | 65 | { 66 | constexpr char* LUA_FILE = R"( 67 | function Pythagoras( a, b ) 68 | return (a * a) + (b * b), a, b 69 | end 70 | )"; 71 | 72 | lua_State* L = luaL_newstate(); 73 | luaL_dostring(L, LUA_FILE); 74 | lua_getglobal(L, "Pythagoras"); 75 | if (lua_isfunction(L, -1)) 76 | { 77 | lua_pushnumber(L, 3); 78 | lua_pushnumber(L, 4); 79 | constexpr int NUM_ARGS = 2; 80 | constexpr int NUM_RETURNS = 3; 81 | lua_pcall(L, NUM_ARGS, NUM_RETURNS, 0); 82 | lua_Number c = lua_tonumber(L, -3); 83 | printf("csqr = %d\n", (int)c); 84 | lua_Number a = lua_tonumber(L, -2); 85 | printf("a = %d\n", (int)a); 86 | lua_Number b = lua_tonumber(L, -1); 87 | printf("b = %d\n", (int)b); 88 | } 89 | lua_close(L); 90 | } 91 | 92 | { 93 | auto NativePythagoras = [](lua_State* L) -> int 94 | { 95 | lua_Number a = lua_tonumber(L, -2); 96 | lua_Number b = lua_tonumber(L, -1); 97 | lua_Number csqr = (a * a) + (b * b); 98 | lua_pushnumber(L, csqr); 99 | return 1; 100 | }; 101 | 102 | constexpr char* LUA_FILE = R"( 103 | function Pythagoras( a, b ) 104 | csqr = NativePythagoras( a, b ) 105 | return csqr, a, b 106 | end 107 | )"; 108 | 109 | lua_State* L = luaL_newstate(); 110 | lua_pushcfunction(L, NativePythagoras); 111 | lua_setglobal(L, "NativePythagoras"); 112 | luaL_dostring(L, LUA_FILE); 113 | lua_getglobal(L, "Pythagoras"); 114 | if (lua_isfunction(L, -1)) 115 | { 116 | lua_pushnumber(L, 3); 117 | lua_pushnumber(L, 4); 118 | constexpr int NUM_ARGS = 2; 119 | constexpr int NUM_RETURNS = 3; 120 | lua_pcall(L, NUM_ARGS, NUM_RETURNS, 0); 121 | lua_Number c = lua_tonumber(L, -3); 122 | printf("csqr = %d\n", (int)c); 123 | lua_Number a = lua_tonumber(L, -2); 124 | printf("a = %d\n", (int)a); 125 | lua_Number b = lua_tonumber(L, -1); 126 | printf("b = %d\n", (int)b); 127 | } 128 | lua_close(L); 129 | } 130 | 131 | // == What you should know by now == 132 | // create and destroy lua state 133 | // get global numbers from lua 134 | // How to use the lua stack from c api 135 | // call lua functions from c 136 | // bind and call c functions from lua 137 | 138 | { 139 | struct Sprite 140 | { 141 | int x; 142 | int y; 143 | 144 | void Move(int velX, int velY) 145 | { 146 | x += velX; 147 | y += velY; 148 | } 149 | }; 150 | 151 | auto CreateSprite = [](lua_State* L) -> int 152 | { 153 | Sprite* sprite = (Sprite*)lua_newuserdata(L, sizeof(Sprite)); 154 | sprite->x = 0; 155 | sprite->y = 0; 156 | return 1; 157 | }; 158 | 159 | auto MoveSprite = [](lua_State* L) -> int 160 | { 161 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 162 | lua_Number velX = lua_tonumber(L, -2); 163 | lua_Number velY = lua_tonumber(L, -1); 164 | sprite->Move((int)velX, (int)velY); 165 | return 0; 166 | }; 167 | 168 | constexpr char* LUA_FILE = R"( 169 | sprite = CreateSprite() 170 | MoveSprite( sprite, 5, 7 ) 171 | )"; 172 | 173 | lua_State* L = luaL_newstate(); 174 | lua_pushcfunction(L, CreateSprite); 175 | lua_setglobal(L, "CreateSprite"); 176 | lua_pushcfunction(L, MoveSprite); 177 | lua_setglobal(L, "MoveSprite"); 178 | luaL_dostring(L, LUA_FILE); 179 | lua_getglobal(L, "sprite"); 180 | if (lua_isuserdata(L, -1)) 181 | { 182 | printf("We got a sprite from Lua\n"); 183 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 184 | printf("x = %d, y = %d\n", sprite->x, sprite->y); 185 | } 186 | else 187 | { 188 | printf("We DIDNT get a sprite from Lua\n"); 189 | } 190 | lua_close(L); 191 | } 192 | 193 | printf("---- tables -----\n"); 194 | 195 | { 196 | constexpr char* LUA_FILE = R"( 197 | x = { dave = "busy", ian = "idle" } 198 | )"; 199 | 200 | lua_State* L = luaL_newstate(); 201 | luaL_dostring(L, LUA_FILE); 202 | 203 | lua_getglobal(L, "x"); 204 | lua_pushstring(L, "dave"); 205 | lua_gettable(L, -2); 206 | const char* daveIs = lua_tostring(L, -1); 207 | printf("daveIs = %s\n", daveIs); 208 | 209 | lua_getglobal(L, "x"); 210 | lua_getfield(L, -1, "ian"); 211 | const char* ianIs = lua_tostring(L, -1); 212 | printf("ianIs = %s\n", ianIs); 213 | 214 | lua_getglobal(L, "x"); 215 | lua_pushstring(L, "sleeping"); 216 | lua_setfield(L, -2, "john"); 217 | 218 | lua_getglobal(L, "x"); 219 | lua_getfield(L, -1, "john"); 220 | const char* johnIs = lua_tostring(L, -1); 221 | printf("johnIs = %s\n", johnIs); 222 | 223 | lua_close(L); 224 | } 225 | 226 | printf("---- metatables and metamethod(s) -----\n"); 227 | { 228 | struct Vec 229 | { 230 | static int CreateVector2D(lua_State* L) 231 | { 232 | lua_newtable(L); 233 | lua_pushstring(L, "x"); 234 | lua_pushnumber(L, 0); 235 | lua_settable(L, -3); 236 | 237 | lua_pushstring(L, "y"); 238 | lua_pushnumber(L, 0); 239 | lua_settable(L, -3); 240 | 241 | luaL_getmetatable(L, "VectorMetaTable"); 242 | lua_setmetatable(L, -2); 243 | 244 | return 1; 245 | } 246 | 247 | static int __add(lua_State* L) 248 | { 249 | printf("__add was called\n"); 250 | assert(lua_istable(L, -2)); // left table 251 | assert(lua_istable(L, -1)); // right table 252 | 253 | lua_pushstring(L, "x"); 254 | lua_gettable(L, -3); 255 | lua_Number xLeft = lua_tonumber(L, -1); 256 | lua_pop(L, 1); 257 | 258 | lua_pushstring(L, "x"); 259 | lua_gettable(L, -2); 260 | lua_Number xRight = lua_tonumber(L, -1); 261 | lua_pop(L, 1); 262 | 263 | lua_Number xAdded = xLeft + xRight; 264 | printf("xAdded = %d\n", (int)xAdded); 265 | 266 | Vec::CreateVector2D(L); 267 | lua_pushstring(L, "x"); 268 | lua_pushnumber(L, xAdded); 269 | lua_rawset(L, -3); 270 | return 1; 271 | } 272 | }; 273 | 274 | constexpr char* LUA_FILE = R"( 275 | v1 = CreateVector() -- v1 is a table 276 | v2 = CreateVector() -- v2 is a table 277 | v1.x = 11 278 | v2.x = 42 279 | v3 = v1 + v2 280 | result = v3.x 281 | )"; 282 | 283 | lua_State* L = luaL_newstate(); 284 | 285 | lua_pushcfunction(L, Vec::CreateVector2D); 286 | lua_setglobal(L, "CreateVector"); 287 | 288 | luaL_newmetatable(L, "VectorMetaTable"); 289 | lua_pushstring(L, "__add"); 290 | lua_pushcfunction(L, Vec::__add); 291 | lua_settable(L, -3); 292 | 293 | int x = luaL_dostring(L, LUA_FILE); 294 | if (x != LUA_OK) 295 | { 296 | printf("Error: %s\n", lua_tostring(L, -1)); 297 | } 298 | 299 | lua_getglobal(L, "result"); 300 | lua_Number result = lua_tonumber(L, -1); 301 | printf("result = %d\n", (int)result); 302 | lua_close(L); 303 | } 304 | 305 | printf("---- C++ constructors and destructors -----\n"); 306 | { 307 | static int numberOfSpritesExisting = 0; 308 | 309 | struct Sprite 310 | { 311 | int x; 312 | int y; 313 | 314 | Sprite() : x(0), y(0) 315 | { 316 | numberOfSpritesExisting++; 317 | } 318 | 319 | ~Sprite() 320 | { 321 | numberOfSpritesExisting--; 322 | } 323 | 324 | void Move(int velX, int velY) 325 | { 326 | x += velX; 327 | y += velY; 328 | } 329 | 330 | void Draw() 331 | { 332 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 333 | } 334 | }; 335 | 336 | auto CreateSprite = [](lua_State* L) -> int 337 | { 338 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 339 | new (pointerToASprite) Sprite(); 340 | luaL_getmetatable(L, "SpriteMetaTable"); 341 | assert(lua_istable(L, -1)); 342 | lua_setmetatable(L, -2); 343 | return 1; 344 | }; 345 | 346 | auto DestroySprite = [](lua_State* L) -> int 347 | { 348 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 349 | sprite->~Sprite(); 350 | return 0; 351 | }; 352 | 353 | auto MoveSprite = [](lua_State* L) -> int 354 | { 355 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 356 | lua_Number velX = lua_tonumber(L, -2); 357 | lua_Number velY = lua_tonumber(L, -1); 358 | sprite->Move((int)velX, (int)velY); 359 | return 0; 360 | }; 361 | 362 | auto DrawSprite = [](lua_State* L) -> int 363 | { 364 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 365 | sprite->Draw(); 366 | return 0; 367 | }; 368 | 369 | constexpr char* LUA_FILE = R"( 370 | sprite = CreateSprite() 371 | MoveSprite( sprite, 5, 7 ) 372 | DrawSprite( sprite ) 373 | MoveSprite( sprite, 1, 2 ) 374 | DrawSprite( sprite ) 375 | 376 | sprite2 = CreateSprite() 377 | MoveSprite( sprite2, 3, 3 ) 378 | DrawSprite( sprite2 ) 379 | )"; 380 | 381 | lua_State* L = luaL_newstate(); 382 | 383 | luaL_newmetatable(L, "SpriteMetaTable"); 384 | lua_pushstring(L, "__gc"); 385 | lua_pushcfunction(L, DestroySprite); 386 | lua_settable(L, -3); 387 | 388 | lua_pushcfunction(L, CreateSprite); 389 | lua_setglobal(L, "CreateSprite"); 390 | lua_pushcfunction(L, MoveSprite); 391 | lua_setglobal(L, "MoveSprite"); 392 | lua_pushcfunction(L, DrawSprite); 393 | lua_setglobal(L, "DrawSprite"); 394 | 395 | int doResult = luaL_dostring(L, LUA_FILE); 396 | if (doResult != LUA_OK) 397 | { 398 | printf("Error: %s\n", lua_tostring(L, -1)); 399 | } 400 | 401 | lua_close(L); 402 | 403 | assert(numberOfSpritesExisting == 0); 404 | } 405 | 406 | printf("---- Object Oriented Access -----\n"); 407 | { 408 | static int numberOfSpritesExisting = 0; 409 | 410 | struct Sprite 411 | { 412 | int x; 413 | int y; 414 | 415 | Sprite() : x(0), y(0) 416 | { 417 | numberOfSpritesExisting++; 418 | } 419 | 420 | ~Sprite() 421 | { 422 | numberOfSpritesExisting--; 423 | } 424 | 425 | void Move(int velX, int velY) 426 | { 427 | x += velX; 428 | y += velY; 429 | } 430 | 431 | void Draw() 432 | { 433 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 434 | } 435 | }; 436 | 437 | auto CreateSprite = [](lua_State* L) -> int 438 | { 439 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 440 | new (pointerToASprite) Sprite(); 441 | luaL_getmetatable(L, "SpriteMetaTable"); 442 | assert(lua_istable(L, -1)); 443 | lua_setmetatable(L, -2); 444 | return 1; 445 | }; 446 | 447 | auto DestroySprite = [](lua_State* L) -> int 448 | { 449 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 450 | sprite->~Sprite(); 451 | return 0; 452 | }; 453 | 454 | auto MoveSprite = [](lua_State* L) -> int 455 | { 456 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 457 | lua_Number velX = lua_tonumber(L, -2); 458 | lua_Number velY = lua_tonumber(L, -1); 459 | sprite->Move((int)velX, (int)velY); 460 | return 0; 461 | }; 462 | 463 | auto DrawSprite = [](lua_State* L) -> int 464 | { 465 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 466 | sprite->Draw(); 467 | return 0; 468 | }; 469 | 470 | constexpr char* LUA_FILE = R"( 471 | sprite = Sprite.new() 472 | sprite:Move( 5, 7 ) -- Sprite.Move( sprite, 5, 7 ) 473 | sprite:Draw() 474 | sprite:Move( 1, 2 ) 475 | sprite:Draw() 476 | 477 | sprite2 = Sprite.new() 478 | sprite2:Move( 3, 3 ) 479 | sprite2:Draw() 480 | 481 | -- sprite -> sprite is a userdatum 482 | -- has a metatable called SpriteMetaTable 483 | -- dont have Move(), use the __index metamethod 484 | -- __index metamethod is a table which is Sprite 485 | -- Sprite has a field called Move(), invoke that 486 | -- Move() is a c function 487 | -- invoke, pass the userdatum as the first parameter. 488 | )"; 489 | 490 | lua_State* L = luaL_newstate(); 491 | 492 | lua_newtable(L); 493 | int spriteTableIdx = lua_gettop(L); 494 | lua_pushvalue(L, spriteTableIdx); 495 | lua_setglobal(L, "Sprite"); 496 | 497 | lua_pushcfunction(L, CreateSprite); 498 | lua_setfield(L, -2, "new"); 499 | lua_pushcfunction(L, MoveSprite); 500 | lua_setfield(L, -2, "Move"); 501 | lua_pushcfunction(L, DrawSprite); 502 | lua_setfield(L, -2, "Draw"); 503 | 504 | luaL_newmetatable(L, "SpriteMetaTable"); 505 | lua_pushstring(L, "__gc"); 506 | lua_pushcfunction(L, DestroySprite); 507 | lua_settable(L, -3); 508 | 509 | lua_pushstring(L, "__index"); 510 | lua_pushvalue(L, spriteTableIdx); 511 | lua_settable(L, -3); 512 | 513 | int doResult = luaL_dostring(L, LUA_FILE); 514 | if (doResult != LUA_OK) 515 | { 516 | printf("Error: %s\n", lua_tostring(L, -1)); 517 | } 518 | 519 | lua_close(L); 520 | 521 | assert(numberOfSpritesExisting == 0); 522 | } 523 | 524 | printf("---- Reading Object Properties -----\n"); 525 | { 526 | static int numberOfSpritesExisting = 0; 527 | 528 | struct Sprite 529 | { 530 | int x; 531 | int y; 532 | 533 | Sprite() : x(0), y(0) 534 | { 535 | numberOfSpritesExisting++; 536 | } 537 | 538 | ~Sprite() 539 | { 540 | numberOfSpritesExisting--; 541 | } 542 | 543 | void Move(int velX, int velY) 544 | { 545 | x += velX; 546 | y += velY; 547 | } 548 | 549 | void Draw() 550 | { 551 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 552 | } 553 | }; 554 | 555 | auto CreateSprite = [](lua_State* L) -> int 556 | { 557 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 558 | new (pointerToASprite) Sprite(); 559 | luaL_getmetatable(L, "SpriteMetaTable"); 560 | assert(lua_istable(L, -1)); 561 | lua_setmetatable(L, -2); 562 | return 1; 563 | }; 564 | 565 | auto DestroySprite = [](lua_State* L) -> int 566 | { 567 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 568 | sprite->~Sprite(); 569 | return 0; 570 | }; 571 | 572 | auto MoveSprite = [](lua_State* L) -> int 573 | { 574 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 575 | lua_Number velX = lua_tonumber(L, -2); 576 | lua_Number velY = lua_tonumber(L, -1); 577 | sprite->Move((int)velX, (int)velY); 578 | return 0; 579 | }; 580 | 581 | auto DrawSprite = [](lua_State* L) -> int 582 | { 583 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 584 | sprite->Draw(); 585 | return 0; 586 | }; 587 | 588 | auto SpriteIndex = [](lua_State* L) -> int 589 | { 590 | assert(lua_isuserdata(L, -2)); 591 | assert(lua_isstring(L, -1)); 592 | 593 | Sprite* sprite = (Sprite*)lua_touserdata(L, -2); 594 | const char* index = lua_tostring(L, -1); 595 | if (strcmp(index, "x") == 0) 596 | { 597 | lua_pushnumber(L, sprite->x); 598 | return 1; 599 | } 600 | else if (strcmp(index, "y") == 0) 601 | { 602 | lua_pushnumber(L, sprite->y); 603 | return 1; 604 | } 605 | else 606 | { 607 | lua_getglobal(L, "Sprite"); 608 | lua_pushstring(L, index); 609 | lua_rawget(L, -2); 610 | return 1; 611 | } 612 | }; 613 | 614 | constexpr char* LUA_FILE = R"( 615 | sprite = Sprite.new() 616 | sprite:Move( 6, 7 ) -- Sprite.Move( sprite, 6, 7 ) 617 | sprite:Draw() 618 | temp_x = sprite.x 619 | )"; 620 | 621 | lua_State* L = luaL_newstate(); 622 | 623 | lua_newtable(L); 624 | int spriteTableIdx = lua_gettop(L); 625 | lua_pushvalue(L, spriteTableIdx); 626 | lua_setglobal(L, "Sprite"); 627 | 628 | lua_pushcfunction(L, CreateSprite); 629 | lua_setfield(L, -2, "new"); 630 | lua_pushcfunction(L, MoveSprite); 631 | lua_setfield(L, -2, "Move"); 632 | lua_pushcfunction(L, DrawSprite); 633 | lua_setfield(L, -2, "Draw"); 634 | 635 | luaL_newmetatable(L, "SpriteMetaTable"); 636 | lua_pushstring(L, "__gc"); 637 | lua_pushcfunction(L, DestroySprite); 638 | lua_settable(L, -3); 639 | 640 | lua_pushstring(L, "__index"); 641 | lua_pushcfunction(L, SpriteIndex); 642 | lua_settable(L, -3); 643 | 644 | int doResult = luaL_dostring(L, LUA_FILE); 645 | if (doResult != LUA_OK) 646 | { 647 | printf("Error: %s\n", lua_tostring(L, -1)); 648 | } 649 | 650 | lua_getglobal(L, "temp_x"); 651 | lua_Number temp_x = lua_tonumber(L, -1); 652 | assert(temp_x == 6); 653 | 654 | lua_close(L); 655 | 656 | assert(numberOfSpritesExisting == 0); 657 | } 658 | 659 | printf("---- Writing Object Properties -----\n"); 660 | { 661 | static int numberOfSpritesExisting = 0; 662 | 663 | struct Sprite 664 | { 665 | int x; 666 | int y; 667 | 668 | Sprite() : x(0), y(0) 669 | { 670 | numberOfSpritesExisting++; 671 | } 672 | 673 | ~Sprite() 674 | { 675 | numberOfSpritesExisting--; 676 | } 677 | 678 | void Move(int velX, int velY) 679 | { 680 | x += velX; 681 | y += velY; 682 | } 683 | 684 | void Draw() 685 | { 686 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 687 | } 688 | }; 689 | 690 | auto CreateSprite = [](lua_State* L) -> int 691 | { 692 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 693 | new (pointerToASprite) Sprite(); 694 | luaL_getmetatable(L, "SpriteMetaTable"); 695 | assert(lua_istable(L, -1)); 696 | lua_setmetatable(L, -2); 697 | return 1; 698 | }; 699 | 700 | auto DestroySprite = [](lua_State* L) -> int 701 | { 702 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 703 | sprite->~Sprite(); 704 | return 0; 705 | }; 706 | 707 | auto MoveSprite = [](lua_State* L) -> int 708 | { 709 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 710 | lua_Number velX = lua_tonumber(L, -2); 711 | lua_Number velY = lua_tonumber(L, -1); 712 | sprite->Move((int)velX, (int)velY); 713 | return 0; 714 | }; 715 | 716 | auto DrawSprite = [](lua_State* L) -> int 717 | { 718 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 719 | sprite->Draw(); 720 | return 0; 721 | }; 722 | 723 | auto SpriteIndex = [](lua_State* L) -> int 724 | { 725 | assert(lua_isuserdata(L, -2)); 726 | assert(lua_isstring(L, -1)); 727 | 728 | Sprite* sprite = (Sprite*)lua_touserdata(L, -2); 729 | const char* index = lua_tostring(L, -1); 730 | if (strcmp(index, "x") == 0) 731 | { 732 | lua_pushnumber(L, sprite->x); 733 | return 1; 734 | } 735 | else if (strcmp(index, "y") == 0) 736 | { 737 | lua_pushnumber(L, sprite->y); 738 | return 1; 739 | } 740 | else 741 | { 742 | lua_getglobal(L, "Sprite"); 743 | lua_pushstring(L, index); 744 | lua_rawget(L, -2); 745 | return 1; 746 | } 747 | }; 748 | 749 | auto SpriteNewIndex = [](lua_State* L) -> int 750 | { 751 | assert(lua_isuserdata(L, -3)); 752 | assert(lua_isstring(L, -2)); 753 | // -1 - value we want to set 754 | 755 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 756 | const char* index = lua_tostring(L, -2); 757 | if (strcmp(index, "x") == 0) 758 | { 759 | sprite->x = (int)lua_tonumber(L, -1); 760 | } 761 | else if (strcmp(index, "y") == 0) 762 | { 763 | sprite->y = (int)lua_tonumber(L, -1); 764 | } 765 | else 766 | { 767 | assert(false); //don't want you to write to my native object!! 768 | } 769 | 770 | return 0; 771 | }; 772 | 773 | constexpr char* LUA_FILE = R"( 774 | sprite = Sprite.new() 775 | sprite:Move( 6, 7 ) -- Sprite.Move( sprite, 6, 7 ) 776 | sprite:Draw() 777 | sprite.y = 10 778 | temp_x = sprite.x 779 | sprite:Draw() 780 | )"; 781 | 782 | lua_State* L = luaL_newstate(); 783 | 784 | lua_newtable(L); 785 | int spriteTableIdx = lua_gettop(L); 786 | lua_pushvalue(L, spriteTableIdx); 787 | lua_setglobal(L, "Sprite"); 788 | 789 | lua_pushcfunction(L, CreateSprite); 790 | lua_setfield(L, -2, "new"); 791 | lua_pushcfunction(L, MoveSprite); 792 | lua_setfield(L, -2, "Move"); 793 | lua_pushcfunction(L, DrawSprite); 794 | lua_setfield(L, -2, "Draw"); 795 | 796 | luaL_newmetatable(L, "SpriteMetaTable"); 797 | lua_pushstring(L, "__gc"); 798 | lua_pushcfunction(L, DestroySprite); 799 | lua_settable(L, -3); 800 | 801 | lua_pushstring(L, "__index"); 802 | lua_pushcfunction(L, SpriteIndex); 803 | lua_settable(L, -3); 804 | 805 | lua_pushstring(L, "__newindex"); 806 | lua_pushcfunction(L, SpriteNewIndex); 807 | lua_settable(L, -3); 808 | 809 | int doResult = luaL_dostring(L, LUA_FILE); 810 | if (doResult != LUA_OK) 811 | { 812 | printf("Error: %s\n", lua_tostring(L, -1)); 813 | } 814 | 815 | lua_getglobal(L, "temp_x"); 816 | lua_Number temp_x = lua_tonumber(L, -1); 817 | assert(temp_x == 6); 818 | 819 | lua_close(L); 820 | 821 | assert(numberOfSpritesExisting == 0); 822 | } 823 | 824 | printf("---- user values -----\n"); 825 | { 826 | static int numberOfSpritesExisting = 0; 827 | 828 | struct Sprite 829 | { 830 | int x; 831 | int y; 832 | 833 | Sprite() : x(0), y(0) 834 | { 835 | numberOfSpritesExisting++; 836 | } 837 | 838 | ~Sprite() 839 | { 840 | numberOfSpritesExisting--; 841 | } 842 | 843 | void Move(int velX, int velY) 844 | { 845 | x += velX; 846 | y += velY; 847 | } 848 | 849 | void Draw() 850 | { 851 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 852 | } 853 | }; 854 | 855 | auto CreateSprite = [](lua_State* L) -> int 856 | { 857 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 858 | new (pointerToASprite) Sprite(); 859 | luaL_getmetatable(L, "SpriteMetaTable"); 860 | assert(lua_istable(L, -1)); 861 | lua_setmetatable(L, -2); 862 | 863 | lua_newtable(L); 864 | lua_setuservalue(L, 1); 865 | 866 | return 1; 867 | }; 868 | 869 | auto DestroySprite = [](lua_State* L) -> int 870 | { 871 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 872 | sprite->~Sprite(); 873 | return 0; 874 | }; 875 | 876 | auto MoveSprite = [](lua_State* L) -> int 877 | { 878 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 879 | lua_Number velX = lua_tonumber(L, -2); 880 | lua_Number velY = lua_tonumber(L, -1); 881 | sprite->Move((int)velX, (int)velY); 882 | return 0; 883 | }; 884 | 885 | auto DrawSprite = [](lua_State* L) -> int 886 | { 887 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 888 | sprite->Draw(); 889 | return 0; 890 | }; 891 | 892 | auto SpriteIndex = [](lua_State* L) -> int 893 | { 894 | assert(lua_isuserdata(L, -2)); //1 895 | assert(lua_isstring(L, -1)); //2 896 | 897 | Sprite* sprite = (Sprite*)lua_touserdata(L, -2); 898 | const char* index = lua_tostring(L, -1); 899 | if (strcmp(index, "x") == 0) 900 | { 901 | lua_pushnumber(L, sprite->x); 902 | return 1; 903 | } 904 | else if (strcmp(index, "y") == 0) 905 | { 906 | lua_pushnumber(L, sprite->y); 907 | return 1; 908 | } 909 | else 910 | { 911 | lua_getuservalue(L, 1); 912 | lua_pushvalue(L, 2); 913 | lua_gettable(L, -2); 914 | if (lua_isnil(L, -1)) 915 | { 916 | lua_getglobal(L, "Sprite"); 917 | lua_pushstring(L, index); 918 | lua_rawget(L, -2); 919 | } 920 | return 1; 921 | } 922 | }; 923 | 924 | auto SpriteNewIndex = [](lua_State* L) -> int 925 | { 926 | assert(lua_isuserdata(L, -3)); //1 927 | assert(lua_isstring(L, -2)); //2 928 | // -1 - value we want to set //3 929 | 930 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 931 | const char* index = lua_tostring(L, -2); 932 | if (strcmp(index, "x") == 0) 933 | { 934 | sprite->x = (int)lua_tonumber(L, -1); 935 | } 936 | else if (strcmp(index, "y") == 0) 937 | { 938 | sprite->y = (int)lua_tonumber(L, -1); 939 | } 940 | else 941 | { 942 | lua_getuservalue(L, 1); //1 943 | lua_pushvalue(L, 2); //2 944 | lua_pushvalue(L, 3); //3 945 | lua_settable(L, -3); //1[2] = 3 946 | } 947 | 948 | return 0; 949 | }; 950 | 951 | constexpr char* LUA_FILE = R"( 952 | sprite = Sprite.new() 953 | sprite:Move( 6, 7 ) -- Sprite.Move( sprite, 6, 7 ) 954 | sprite:Draw() 955 | sprite.y = 10 956 | sprite.zzz = 99 957 | sprite.x = sprite.zzz 958 | temp_x = sprite.x 959 | sprite:Draw() 960 | )"; 961 | 962 | constexpr int POOL_SIZE = 1024 * 10; 963 | char memory[POOL_SIZE]; 964 | ArenaAllocator pool(memory, &memory[POOL_SIZE - 1]); 965 | //GlobalAllocator pool; 966 | //for (int i = 0; i < 50000; i++) //comment this line in for benchmarking the mem allocator 967 | { 968 | pool.Reset(); 969 | lua_State* L = lua_newstate(ArenaAllocator::l_alloc, &pool); 970 | //lua_State* L = luaL_newstate(); 971 | //lua_State* L = lua_newstate(GlobalAllocator::l_alloc, &pool); 972 | 973 | lua_newtable(L); 974 | int spriteTableIdx = lua_gettop(L); 975 | lua_pushvalue(L, spriteTableIdx); 976 | lua_setglobal(L, "Sprite"); 977 | 978 | lua_pushcfunction(L, CreateSprite); 979 | lua_setfield(L, -2, "new"); 980 | lua_pushcfunction(L, MoveSprite); 981 | lua_setfield(L, -2, "Move"); 982 | lua_pushcfunction(L, DrawSprite); 983 | lua_setfield(L, -2, "Draw"); 984 | 985 | luaL_newmetatable(L, "SpriteMetaTable"); 986 | lua_pushstring(L, "__gc"); 987 | lua_pushcfunction(L, DestroySprite); 988 | lua_settable(L, -3); 989 | 990 | lua_pushstring(L, "__index"); 991 | lua_pushcfunction(L, SpriteIndex); 992 | lua_settable(L, -3); 993 | 994 | lua_pushstring(L, "__newindex"); 995 | lua_pushcfunction(L, SpriteNewIndex); 996 | lua_settable(L, -3); 997 | 998 | int doResult = luaL_dostring(L, LUA_FILE); 999 | if (doResult != LUA_OK) 1000 | { 1001 | printf("Error: %s\n", lua_tostring(L, -1)); 1002 | } 1003 | 1004 | lua_getglobal(L, "temp_x"); 1005 | lua_Number temp_x = lua_tonumber(L, -1); 1006 | assert(temp_x == 99); 1007 | 1008 | lua_close(L); 1009 | } 1010 | 1011 | assert(numberOfSpritesExisting == 0); 1012 | } 1013 | 1014 | printf("---- lua memory allocation -----\n"); 1015 | { 1016 | struct LuaMem 1017 | { 1018 | static void *l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { 1019 | (void)ud; (void)osize; /* not used */ 1020 | if (nsize == 0) { 1021 | free(ptr); 1022 | return NULL; 1023 | } 1024 | else 1025 | return realloc(ptr, nsize); 1026 | } 1027 | }; 1028 | 1029 | void* ud = nullptr; 1030 | lua_State* L = lua_newstate( LuaMem::l_alloc, ud ); 1031 | assert(L != nullptr); 1032 | lua_close(L); 1033 | } 1034 | 1035 | printf("---- lua arena memory allocation -----\n"); 1036 | { 1037 | constexpr int POOL_SIZE = 1024 * 10; 1038 | char memory[POOL_SIZE]; 1039 | ArenaAllocator pool(memory, &memory[POOL_SIZE-1] ); 1040 | lua_State* L = lua_newstate(ArenaAllocator::l_alloc, &pool); 1041 | assert(L != nullptr); 1042 | lua_close(L); 1043 | } 1044 | 1045 | printf("---- lua arena aligned memory allocation -----\n"); 1046 | { 1047 | constexpr int POOL_SIZE = 1024 * 10; 1048 | char memory[POOL_SIZE]; 1049 | ArenaAllocator pool(memory, &memory[POOL_SIZE - 1]); 1050 | lua_State* L = lua_newstate(ArenaAllocator::l_alloc, &pool); 1051 | 1052 | struct alignas(8) Thing 1053 | { 1054 | float x; 1055 | float z; 1056 | }; 1057 | 1058 | Thing* t = (Thing*)lua_newuserdata(L, sizeof(Thing) ); 1059 | assert( (uintptr_t)t % alignof(Thing) == 0); //are we aligned??? 1060 | 1061 | assert(L != nullptr); 1062 | lua_close(L); 1063 | } 1064 | 1065 | printf("---- upvalues & lightuserdata -----\n"); 1066 | { 1067 | struct Sprite 1068 | { 1069 | int x; 1070 | int y; 1071 | 1072 | Sprite() : x(0), y(0) {} 1073 | ~Sprite() {} 1074 | 1075 | void Move(int velX, int velY) 1076 | { 1077 | x += velX; 1078 | y += velY; 1079 | } 1080 | 1081 | void Draw() 1082 | { 1083 | printf("sprite(%p): x = %d, y = %d\n", this, x, y); 1084 | } 1085 | }; 1086 | 1087 | struct SpriteManager 1088 | { 1089 | std::vector m_sprites; 1090 | int numberOfSpritesExisting = 0; 1091 | int numberOfSpritesMade = 0; 1092 | 1093 | void LookAfterSprite(Sprite* sprite) 1094 | { 1095 | numberOfSpritesExisting++; 1096 | numberOfSpritesMade++; 1097 | m_sprites.push_back(sprite); 1098 | } 1099 | 1100 | void ForgetSprite(Sprite* sprite) 1101 | { 1102 | int i = 0; 1103 | for (auto& s : m_sprites) 1104 | { 1105 | if (s == sprite) 1106 | { 1107 | numberOfSpritesExisting--; 1108 | m_sprites.erase(m_sprites.begin() + i); 1109 | return; 1110 | } 1111 | i++; 1112 | } 1113 | } 1114 | }; 1115 | 1116 | SpriteManager spriteManager; 1117 | 1118 | auto CreateSprite = [](lua_State* L) -> int 1119 | { 1120 | SpriteManager* sm = (SpriteManager*)lua_touserdata(L, lua_upvalueindex(1)); 1121 | assert(sm); 1122 | 1123 | void* pointerToASprite = lua_newuserdata(L, sizeof(Sprite)); 1124 | new (pointerToASprite) Sprite(); 1125 | luaL_getmetatable(L, "SpriteMetaTable"); 1126 | assert(lua_istable(L, -1)); 1127 | lua_setmetatable(L, -2); 1128 | 1129 | lua_newtable(L); 1130 | lua_setuservalue(L, 1); 1131 | 1132 | sm->LookAfterSprite((Sprite*)pointerToASprite); 1133 | 1134 | return 1; 1135 | }; 1136 | 1137 | auto DestroySprite = [](lua_State* L) -> int 1138 | { 1139 | SpriteManager* sm = (SpriteManager*)lua_touserdata(L, lua_upvalueindex(1)); 1140 | assert(sm); 1141 | 1142 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 1143 | sm->ForgetSprite(sprite); 1144 | sprite->~Sprite(); 1145 | return 0; 1146 | }; 1147 | 1148 | auto MoveSprite = [](lua_State* L) -> int 1149 | { 1150 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 1151 | lua_Number velX = lua_tonumber(L, -2); 1152 | lua_Number velY = lua_tonumber(L, -1); 1153 | sprite->Move((int)velX, (int)velY); 1154 | return 0; 1155 | }; 1156 | 1157 | auto DrawSprite = [](lua_State* L) -> int 1158 | { 1159 | Sprite* sprite = (Sprite*)lua_touserdata(L, -1); 1160 | sprite->Draw(); 1161 | return 0; 1162 | }; 1163 | 1164 | auto SpriteIndex = [](lua_State* L) -> int 1165 | { 1166 | assert(lua_isuserdata(L, -2)); //1 1167 | assert(lua_isstring(L, -1)); //2 1168 | 1169 | Sprite* sprite = (Sprite*)lua_touserdata(L, -2); 1170 | const char* index = lua_tostring(L, -1); 1171 | if (strcmp(index, "x") == 0) 1172 | { 1173 | lua_pushnumber(L, sprite->x); 1174 | return 1; 1175 | } 1176 | else if (strcmp(index, "y") == 0) 1177 | { 1178 | lua_pushnumber(L, sprite->y); 1179 | return 1; 1180 | } 1181 | else 1182 | { 1183 | lua_getuservalue(L, 1); 1184 | lua_pushvalue(L, 2); 1185 | lua_gettable(L, -2); 1186 | if (lua_isnil(L, -1)) 1187 | { 1188 | lua_getglobal(L, "Sprite"); 1189 | lua_pushstring(L, index); 1190 | lua_rawget(L, -2); 1191 | } 1192 | return 1; 1193 | } 1194 | }; 1195 | 1196 | auto SpriteNewIndex = [](lua_State* L) -> int 1197 | { 1198 | assert(lua_isuserdata(L, -3)); //1 1199 | assert(lua_isstring(L, -2)); //2 1200 | // -1 - value we want to set //3 1201 | 1202 | Sprite* sprite = (Sprite*)lua_touserdata(L, -3); 1203 | const char* index = lua_tostring(L, -2); 1204 | if (strcmp(index, "x") == 0) 1205 | { 1206 | sprite->x = (int)lua_tonumber(L, -1); 1207 | } 1208 | else if (strcmp(index, "y") == 0) 1209 | { 1210 | sprite->y = (int)lua_tonumber(L, -1); 1211 | } 1212 | else 1213 | { 1214 | lua_getuservalue(L, 1); //1 1215 | lua_pushvalue(L, 2); //2 1216 | lua_pushvalue(L, 3); //3 1217 | lua_settable(L, -3); //1[2] = 3 1218 | } 1219 | 1220 | return 0; 1221 | }; 1222 | 1223 | constexpr char* LUA_FILE = R"( 1224 | sprite = Sprite.new() 1225 | sprite:Move( 6, 7 ) -- Sprite.Move( sprite, 6, 7 ) 1226 | sprite:Draw() 1227 | sprite.y = 10 1228 | sprite.zzz = 99 1229 | sprite.x = sprite.zzz 1230 | sprite:Draw() 1231 | Sprite.new() 1232 | Sprite.new() 1233 | )"; 1234 | 1235 | constexpr int POOL_SIZE = 1024 * 10; 1236 | char memory[POOL_SIZE]; 1237 | ArenaAllocator pool(memory, &memory[POOL_SIZE - 1]); 1238 | lua_State* L = lua_newstate(ArenaAllocator::l_alloc, &pool); 1239 | 1240 | lua_newtable(L); 1241 | int spriteTableIdx = lua_gettop(L); 1242 | lua_pushvalue(L, spriteTableIdx); 1243 | lua_setglobal(L, "Sprite"); 1244 | 1245 | constexpr int NUMBER_OF_UPVALUES = 1; 1246 | lua_pushlightuserdata(L, &spriteManager); 1247 | lua_pushcclosure(L, CreateSprite, NUMBER_OF_UPVALUES); 1248 | lua_setfield(L, -2, "new"); 1249 | lua_pushcfunction(L, MoveSprite); 1250 | lua_setfield(L, -2, "Move"); 1251 | lua_pushcfunction(L, DrawSprite); 1252 | lua_setfield(L, -2, "Draw"); 1253 | 1254 | luaL_newmetatable(L, "SpriteMetaTable"); 1255 | lua_pushstring(L, "__gc"); 1256 | lua_pushlightuserdata(L, &spriteManager); 1257 | lua_pushcclosure(L, DestroySprite, NUMBER_OF_UPVALUES); 1258 | lua_settable(L, -3); 1259 | 1260 | lua_pushstring(L, "__index"); 1261 | lua_pushcfunction(L, SpriteIndex); 1262 | lua_settable(L, -3); 1263 | 1264 | lua_pushstring(L, "__newindex"); 1265 | lua_pushcfunction(L, SpriteNewIndex); 1266 | lua_settable(L, -3); 1267 | 1268 | int doResult = luaL_dostring(L, LUA_FILE); 1269 | if (doResult != LUA_OK) 1270 | { 1271 | printf("Error: %s\n", lua_tostring(L, -1)); 1272 | } 1273 | 1274 | lua_close(L); 1275 | 1276 | assert(spriteManager.numberOfSpritesExisting == 0); 1277 | assert(spriteManager.numberOfSpritesMade == 3); 1278 | } 1279 | 1280 | extern void AutomatedBindingTutorial(); 1281 | AutomatedBindingTutorial(); 1282 | } -------------------------------------------------------------------------------- /make_VS2015.bat: -------------------------------------------------------------------------------- 1 | md build 2 | cd build 3 | cmake -G "Visual Studio 14 2015 Win64" ..\ -Dgtest_force_shared_crt=on 4 | PAUSE --------------------------------------------------------------------------------