Skip to content

Commit

Permalink
Add LuaBridge library.
Browse files Browse the repository at this point in the history
Add some docs on how to add C++ classes to Lua.
  • Loading branch information
vieiraa committed Mar 19, 2020
1 parent 7d7987e commit 66f38f6
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 234 deletions.
8 changes: 4 additions & 4 deletions apps/freeablo/fagui/console/console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ namespace FAGui
static const char* nullFile = "/dev/null";
#endif
static char consoleStdoutBuffer[BUFSIZ];
std::unique_ptr<Console> Console::mInstance = nullptr;
std::shared_ptr<Console> Console::mInstance = nullptr;
static std::stringstream consoleCout;

Console::Console() : mBuffer({}), bufferLen(0), inputLen(0), mScript({})
Console::Console() : mBuffer({}), bufferLen(0), inputLen(0), mScript(Script::LuaScript::getInstance())
{
std::cout.rdbuf(consoleCout.rdbuf());
freopen(nullFile, "a", stdout);
Expand All @@ -26,7 +26,7 @@ namespace FAGui
fclose(stdout);
}

std::unique_ptr<Console>& Console::getInstance()
std::shared_ptr<Console> Console::getInstance()
{
if (!mInstance)
mInstance = std::unique_ptr<Console>(new Console());
Expand Down Expand Up @@ -61,7 +61,7 @@ namespace FAGui
bufferLen += inputLen;
inputLen = 0;

mScript.eval(command.c_str());
mScript->eval(command.c_str());
std::string msg = ">> ";
msg.append(consoleStdoutBuffer, strlen(consoleStdoutBuffer));
std::memset(consoleStdoutBuffer, '\0', BUFSIZ);
Expand Down
6 changes: 3 additions & 3 deletions apps/freeablo/fagui/console/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ namespace FAGui
static constexpr size_t inputSize = 512;
char mInput[inputSize];
int inputLen;
Script::LuaScript mScript;
static std::unique_ptr<Console> mInstance;
std::shared_ptr<Script::LuaScript> mScript;
static std::shared_ptr<Console> mInstance;

public:
static std::unique_ptr<Console>& getInstance();
static std::shared_ptr<Console> getInstance();
~Console();

char* getInput() { return mInput; };
Expand Down
2 changes: 1 addition & 1 deletion apps/freeablo/fagui/guimanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ namespace FAGui

void GuiManager::consolePanel(nk_context* ctx)
{
auto& c = Console::getInstance();
auto c = Console::getInstance();
drawPanel(ctx,
PanelType::console,
[&]() {
Expand Down
2 changes: 1 addition & 1 deletion components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,5 @@ add_library(Script
script/luascript.h
)

target_link_libraries(Script lua Filesystem Misc)
target_link_libraries(Script lua LuaBridge Misc)
set_target_properties(Script PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS}")
116 changes: 5 additions & 111 deletions components/script/luascript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace Script
{
std::shared_ptr<LuaScript> LuaScript::mInstance = nullptr;

LuaScript::LuaScript() : mState(luaL_newstate()) { luaL_openlibs(mState); }

LuaScript::~LuaScript()
Expand All @@ -13,117 +15,6 @@ namespace Script
lua_close(mState);
}

void LuaScript::printError(const std::string& variable, const std::string& reason)
{
#ifdef NDEBUG
(void)variable;
(void)reason;
#else
std::cerr << "Can't get variable " << variable << ".\nReason: " << reason << "\n";
#endif
}

template <> std::string LuaScript::luaGetDefault<std::string>() { return "null"; }

template <> bool LuaScript::luaGet<bool>(const std::string& variable)
{
(void)variable;
return static_cast<bool>(lua_toboolean(mState, -1));
}
template <> int LuaScript::luaGet<int>(const std::string& variable)
{
(void)variable;
return static_cast<int>(lua_tointeger(mState, -1));
}
template <> int64_t LuaScript::luaGet<int64_t>(const std::string& variable)
{
(void)variable;
return static_cast<int64_t>(lua_tointeger(mState, -1));
}

template <> FixedPoint LuaScript::luaGet<FixedPoint>(const std::string& variable)
{
(void)variable;
return static_cast<FixedPoint>(luaL_checknumber(mState, 1));
}

template <> std::string LuaScript::luaGet<std::string>(const std::string& variable)
{
std::string ret = "null";

release_assert(lua_isstring(mState, -1));

if (lua_isstring(mState, -1))
ret = std::string(lua_tostring(mState, -1));
else
printError(variable, "Not a string");

return ret;
}

bool LuaScript::luaGetToStack(const std::string& variable, int& level)
{
level = 0;
std::string var{};

for (char c : variable)
{
if (c == '.')
{
if (level == 0)
{
lua_getglobal(mState, var.c_str());
}

else
{
lua_getfield(mState, -1, var.c_str());
}

if (lua_isnil(mState, -1))
{
printError(variable, var + " is not defined");
return false;
}

else
{
var = "";
++level;
}
}

else
{
var += c;
}
}

if (level == 0)
{
lua_getglobal(mState, var.c_str());
}

else
{
lua_getfield(mState, -1, var.c_str());
}

if (lua_isnil(mState, -1))
{
printError(variable, var + " is not defined");
return false;
}

return true;
}

void LuaScript::clean()
{
int n = lua_gettop(mState);
lua_pop(mState, n);
}

void LuaScript::runScript(const std::string& path)
{
lua_settop(mState, 0);
Expand All @@ -146,4 +37,7 @@ namespace Script
lua_pop(mState, 1);
}
}

template <> float LuaScript::get<float>(const std::string &variable) = delete;
template <> double LuaScript::get<double>(const std::string &variable) = delete;
}
92 changes: 20 additions & 72 deletions components/script/luascript.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,105 +11,53 @@
#include "lua.h"
#include "lualib.h"

#include "LuaBridge/LuaBridge.h"
#include "LuaBridge/Vector.h"

namespace Script
{
class LuaScript
{
lua_State* mState;
static std::shared_ptr<LuaScript> mInstance;

public:
LuaScript();
~LuaScript();

template <typename T> void registerGlobalType();
template <typename T> void registerType();

template <typename Func> void registerGlobalFunction(const std::string& functionName, Func&& func)
{
lua_pushcfunction(mState, func);
lua_setglobal(mState, functionName.c_str());
luabridge::getGlobalNamespace(mState).addFunction(functionName.c_str(), func);
}

template <typename T> void pushObject(T& obj, const char* varName)
template <typename T> void pushGlobalObject(T& obj, const char* varName)
{
static bool registered = false;
if (!registered)
{
registerGlobalType<T>();
registered = true;
}

T** udata = reinterpret_cast<T**>(lua_newuserdata(mState, sizeof(T*)));
(*udata) = &obj;
const std::string typeName = typeid(T).name();
luaL_setmetatable(mState, typeName.substr(1).c_str());
registerType<T>();
luabridge::push(mState, obj);
lua_setglobal(mState, varName);
}

template <typename T> T get(const std::string& variable)
{
release_assert(mState);
int level;

T result;
if (luaGetToStack(variable, level))
result = luaGet<T>(variable);

else
result = luaGetDefault<T>();

lua_pop(mState, level + 1);
return result;
}

template <typename T> std::vector<T> getVector(const std::string& variable)
{
std::vector<T> ret;
lua_getglobal(mState, variable.c_str());
if (lua_isnil(mState, -1))
{
return {};
}

lua_pushnil(mState);

while (lua_next(mState, -2))
{
T aux;
if constexpr (std::is_same<T, std::string>::value)
{
ret.push_back(lua_tostring(mState, -1));
}

else
{
ret.push_back(static_cast<T>(lua_tonumber(mState, -1)));
}

lua_pop(mState, 1);
}

clean();
return ret;
luabridge::LuaRef ret = luabridge::getGlobal(mState, variable.c_str());
return ret.cast<T>();
}

void runScript(const std::string& path);
void eval(const char* script);

private:
bool luaGetToStack(const std::string& variable, int& level);

void clean();

void printError(const std::string& variable, const std::string& reason);
static std::shared_ptr<LuaScript> getInstance()
{
if (!mInstance)
mInstance = std::shared_ptr<LuaScript>(new LuaScript());

template <typename T> T luaGet(const std::string& variable = "");
return mInstance;
}

template <typename T> T luaGetDefault() { return {}; }
operator lua_State*() { return mState; }

template <typename Func> void pushFunction(Func&& f, const char* funcName)
{
lua_pushcfunction(mState, f);
lua_setfield(mState, -2, funcName);
}
private:
LuaScript();
};
}
47 changes: 47 additions & 0 deletions docs/cpp_class_to_lua.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Registering a C++ class into Lua

Suppose you have the following class:

```cpp
class Greeter
{
const char* msg
public:
Greeter() {}
Greeter(const char *msg) {}
void hello(const char* name) { std::cout << "Hello, " << name << "\n"; }
void print() { std::cout << msg << "\n"; }
};
```

If you want to use it inside Lua, you should implement Script::LuaScript::registerType for it:

```cpp
namespace Script
{
template <> void LuaScript::registerType<Greeter>()
{
luabridge::getGlobalNamespace(mState)
.beginNamespace("example") // optionally, you can wrap the class inside a namespace
.beginClass<Greeter>("Greeter")
.addConstructor<void (*)(void)>()
.addConstructor<void (*)(const char*)>()
.addFunction("hello", &Greeter::hello)
.addFunction("print", &Greeter::print)
.endClass()
.endNamespace();
}
}
```
Make sure you call 'registerType' before using the class in Lua. Now, to use in Lua:
```lua
local greeter = example.Greeter()
greeter:hello("John Smith")
local greeter2 = example.Greeter("Hello world")
greeter2:print()
```

For more information, see [LuaBridge Reference](https://vinniefalco.github.io/LuaBridge/Manual.html).
1 change: 1 addition & 0 deletions extern/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ add_subdirectory(SDL_image)
add_subdirectory(StormLib)
add_subdirectory(gtest)
add_subdirectory(lua)
add_subdirectory(LuaBridge)

unset(MESSAGE_QUIET)
Loading

0 comments on commit 66f38f6

Please sign in to comment.