├── ghost.lua ├── Component.h ├── Entity.cpp ├── .gitignore ├── LuaHelperFunctions.h ├── NpcComponent.h ├── NpcComponent.cpp ├── GraphicsComponent.h ├── GraphicsComponent.cpp ├── Entity.h ├── README.md ├── main.cpp └── LuaHelperFunctions.cpp /ghost.lua: -------------------------------------------------------------------------------- 1 | ghost = { 2 | GraphicsComponent = { 3 | filename = "ghost.png" 4 | }, 5 | NpcComponent = { 6 | phrase = "I'M A SCARY GHOST!!!" 7 | } 8 | } -------------------------------------------------------------------------------- /Component.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace luabridge { 4 | class LuaRef; 5 | } // forward declaration 6 | 7 | class Component { 8 | public: 9 | virtual ~Component() {}; 10 | }; -------------------------------------------------------------------------------- /Entity.cpp: -------------------------------------------------------------------------------- 1 | #include "Entity.h" 2 | #include "Component.h" 3 | 4 | Entity::~Entity() { 5 | for (auto& c : components) { 6 | delete c.second; 7 | } 8 | } 9 | 10 | void Entity::addComponent(std::type_index type, Component* c) { 11 | components[type] = c; 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.obj 3 | *.XML 4 | *.user 5 | *.tlog 6 | *.log 7 | *.htm 8 | *.res 9 | *.manifest 10 | *.lastbuildstate 11 | *.pdb 12 | *.rc 13 | *.dep 14 | *.css 15 | *.xslt 16 | *.idb 17 | *.ipch 18 | *.vcproj 19 | *.vcxproj 20 | *.sln 21 | *.filters 22 | *.suo 23 | *.sdf 24 | *.svn 25 | *.pro 26 | _UpgradeReport_Files/ 27 | Debug/ 28 | Release/ 29 | ipch/ -------------------------------------------------------------------------------- /LuaHelperFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct lua_State; 6 | 7 | namespace luah { 8 | bool loadScript(lua_State* L, const std::string& filename); 9 | void lua_gettostack(lua_State* L, const std::string& variableName); 10 | void loadGetKeysFunction(lua_State* L); 11 | std::vector getTableKeys(lua_State* L, const std::string& name); 12 | } 13 | -------------------------------------------------------------------------------- /NpcComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Component.h" 5 | 6 | class NpcComponent : public Component { 7 | public: 8 | NpcComponent(luabridge::LuaRef& componentTable); 9 | 10 | void setPhrase(const std::string& phrase) { 11 | this->phrase = phrase; 12 | } 13 | 14 | std::string getPhrase() const { 15 | return phrase; 16 | } 17 | private: 18 | std::string phrase; 19 | }; -------------------------------------------------------------------------------- /NpcComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "NpcComponent.h" 2 | #include 3 | #include 4 | 5 | NpcComponent::NpcComponent(luabridge::LuaRef& NpcTable) { 6 | using namespace luabridge; 7 | auto phraseRef = NpcTable["phrase"]; 8 | if (phraseRef.isString()) { 9 | phrase = phraseRef.cast(); 10 | } else { 11 | std::cout << "Error, NpcComponent.phrase is not a string!" << std::endl; 12 | } 13 | } -------------------------------------------------------------------------------- /GraphicsComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Component.h" 5 | 6 | class GraphicsComponent : public Component { 7 | public: 8 | GraphicsComponent(luabridge::LuaRef& componentTable); 9 | 10 | void setFilename(const std::string& filename) { 11 | this->filename = filename; 12 | } 13 | 14 | std::string getFilename() const { 15 | return filename; 16 | } 17 | private: 18 | std::string filename; 19 | }; -------------------------------------------------------------------------------- /GraphicsComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphicsComponent.h" 2 | #include 3 | #include 4 | 5 | GraphicsComponent::GraphicsComponent(luabridge::LuaRef& componentTable) { 6 | using namespace luabridge; 7 | auto filenameRef = componentTable["filename"]; 8 | if (filenameRef.isString()) { 9 | filename = filenameRef.cast(); 10 | } else { 11 | std::cout << "Error, GraphicsComponent.filename is not a string!" << std::endl; 12 | } 13 | } -------------------------------------------------------------------------------- /Entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class Component; 8 | 9 | class Entity { 10 | public: 11 | ~Entity(); 12 | void addComponent(std::type_index type, Component* c); 13 | 14 | template 15 | T* get() { 16 | auto it = components.find(std::type_index(typeid(T))); 17 | if (it != components.end()) { 18 | return dynamic_cast(it->second); 19 | } 20 | return nullptr; 21 | } 22 | 23 | void setType(const std::string& type) { 24 | this->type = type; 25 | } 26 | 27 | std::string getType() const { 28 | return type; 29 | } 30 | private: 31 | std::string type; 32 | std::map components; 33 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Using Lua with C++ in Practice part 2 source code 2 | ================== 3 | 4 | Source code for part 2 of "Using Lua with C++ in practice" 5 | 6 | Dependencies: 7 | 8 | * C++11 9 | * Lua 10 | * [LuaBridge][1] 11 | 12 | 13 | **Tutorials** 14 | 15 | **Part1**: https://eliasdaler.wordpress.com/2015/08/10/using-lua-and-cpp-in-practice/ 16 | 17 | **Part2**: https://eliasdaler.wordpress.com/2015/09/08/using-lua-with-cpp-in-practice-part-2/ 18 | 19 | 20 | This code uses zlib license. 21 | 22 | Copyright (C) 2015 Elias Daler 23 | 24 | This software is provided 'as-is', without any express or implied 25 | warranty. In no event will the authors be held liable for any damages 26 | arising from the use of this software. 27 | 28 | Permission is granted to anyone to use this software for any purpose, 29 | including commercial applications, and to alter it and redistribute it 30 | freely, subject to the following restrictions: 31 | 32 | 1. The origin of this software must not be misrepresented; you must not 33 | claim that you wrote the original software. If you use this software 34 | in a product, an acknowledgment in the product documentation would be 35 | appreciated but is not required. 36 | 2. Altered source versions must be plainly marked as such, and must not be 37 | misrepresented as being the original software. 38 | 3. This notice may not be removed or altered from any source distribution. 39 | 40 | [1]: https://github.com/vinniefalco/LuaBridge "LuaBridge" -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "LuaHelperFunctions.h" 4 | 5 | #include "GraphicsComponent.h" 6 | #include "NpcComponent.h" 7 | #include "Entity.h" 8 | 9 | extern "C" { 10 | # include "lua.h" 11 | # include "lauxlib.h" 12 | # include "lualib.h" 13 | } 14 | 15 | #include 16 | 17 | 18 | template 19 | void addComponent(Entity* e, luabridge::LuaRef& componentTable) { 20 | e->addComponent(std::type_index(typeid(T)), new T(componentTable)); 21 | } 22 | 23 | Entity* loadEntity(lua_State* L, const std::string& type) { 24 | using namespace luabridge; 25 | auto e = new Entity(); 26 | e->setType(type); 27 | auto v = luah::getTableKeys(L, type); 28 | LuaRef entityTable = getGlobal(L, type.c_str()); 29 | for (auto& componentName : v) { 30 | if (componentName == "GraphicsComponent") { 31 | LuaRef gcTable = entityTable["GraphicsComponent"]; 32 | addComponent(e, gcTable); 33 | } else if (componentName == "NpcComponent") { 34 | LuaRef npccTable = entityTable["NpcComponent"]; 35 | addComponent(e, npccTable); 36 | } 37 | 38 | std::cout << "Added " << componentName << " to " << type << std::endl; 39 | } 40 | return e; 41 | } 42 | 43 | int main() { 44 | lua_State* L = luaL_newstate(); 45 | luaL_openlibs(L); 46 | 47 | luah::loadScript(L, "ghost.lua"); 48 | luah::loadGetKeysFunction(L); 49 | 50 | auto e = loadEntity(L, "ghost"); 51 | auto npcc = e->get(); 52 | std::cout << e->getType() << " says: " << npcc->getPhrase() << std::endl; 53 | 54 | lua_close(L); 55 | delete e; 56 | } -------------------------------------------------------------------------------- /LuaHelperFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "LuaHelperFunctions.h" 2 | #include 3 | #include 4 | 5 | extern "C" { 6 | # include "lua.h" 7 | # include "lauxlib.h" 8 | # include "lualib.h" 9 | } 10 | 11 | bool luah::loadScript(lua_State* L, const std::string& filename) { 12 | if (!(luaL_loadfile(L, filename.c_str()) || lua_pcall(L, 0, 0, 0))) { 13 | return true; 14 | } else { 15 | std::cout << "Failed to load " << filename << std::endl; 16 | std::cout << lua_tostring(L, -1) << std::endl; 17 | lua_pop(L, 1); // pop the error message from stack 18 | return false; 19 | } 20 | } 21 | 22 | void luah::lua_gettostack(lua_State* L, const std::string& variableName) { 23 | int level = 0; 24 | std::string var = ""; 25 | for (unsigned int i = 0; i < variableName.size(); i++) { 26 | if (variableName.at(i) == '.') { 27 | if (level == 0) { 28 | lua_getglobal(L, var.c_str()); 29 | } else { 30 | lua_getfield(L, -1, var.c_str()); 31 | } 32 | 33 | if (lua_isnil(L, -1)) { 34 | std::cout << "Error, can't get " << variableName << std::endl; 35 | return; 36 | } else { 37 | var = ""; 38 | ++level; 39 | } 40 | } else { 41 | var += variableName.at(i); 42 | } 43 | } 44 | if (level == 0) { 45 | lua_getglobal(L, var.c_str()); 46 | } else { 47 | lua_getfield(L, -1, var.c_str()); 48 | } 49 | 50 | if (level == 0) { return; } // no need to remove anything 51 | 52 | int tableIndex = lua_gettop(L) - level; 53 | lua_replace(L, tableIndex); 54 | lua_settop(L, tableIndex); 55 | } 56 | 57 | void luah::loadGetKeysFunction(lua_State* L) { 58 | std::string code = 59 | R"(function getKeys(t) 60 | s = {} 61 | for k, v in pairs(t) do 62 | table.insert(s, k) 63 | end 64 | return s 65 | end)"; 66 | luaL_dostring(L, code.c_str()); 67 | } 68 | 69 | std::vector luah::getTableKeys(lua_State* L, const std::string& name) { 70 | lua_getglobal(L, "getKeys"); // get function 71 | if (lua_isnil(L, -1)) { 72 | std::cout << "Get keys function is not loaded. Loading..." << std::endl; 73 | loadGetKeysFunction(L); 74 | lua_getglobal(L, "getKeys"); 75 | } 76 | 77 | lua_gettostack(L, name); 78 | 79 | lua_pcall(L, 1, 1, 0); // execute function. Got table on stack 80 | 81 | lua_pushnil(L); 82 | 83 | std::vector keys; 84 | 85 | while (lua_next(L, -2)) { // get values one by one 86 | if (lua_type(L, -1) == LUA_TSTRING) { 87 | keys.push_back(lua_tostring(L, -1)); 88 | } 89 | lua_pop(L, 1); 90 | } 91 | 92 | lua_settop(L, 0); // remove s table from stack 93 | return keys; 94 | } --------------------------------------------------------------------------------