Skip to content

Commit

Permalink
[lua] formalised lua api c++-side
Browse files Browse the repository at this point in the history
  • Loading branch information
harrand committed Aug 28, 2023
1 parent c3c0398 commit 30b3c48
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 73 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ add_library(topaz STATIC
src/tz/io/image.hpp

# tz::lua
src/tz/lua/api.cpp
src/tz/lua/api.hpp
src/tz/lua/lua.hpp
src/tz/lua/state.cpp
Expand Down
72 changes: 72 additions & 0 deletions src/tz/lua/api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "tz/lua/api.hpp"
#include "tz/dbgui/dbgui.hpp"
#include <sstream>
#include <filesystem>

extern "C"
{
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"
}

namespace tz::lua
{
LUA_BEGIN(assert)
auto st = reinterpret_cast<lua_State*>(s);

bool b = state.stack_get_bool(1);
std::string stack = state.collect_stack();
lua_Debug ar;
lua_getstack(st, 1, &ar);
lua_getinfo(st, "nSl", &ar);
if(!b && TZ_DEBUG)
{
tz::dbgui::add_to_lua_log("<<Lua Assert Failure Detected>>");
}
tz::assert(b, "Lua Assertion Failure: ```lua\n\n%s\n\n```\nOn line %d\nStack:\n%s", ar.source, ar.currentline, stack.c_str());
return 0;
LUA_END

LUA_BEGIN(error)
auto st = reinterpret_cast<lua_State*>(s);
lua_pushboolean(st, false);
LUA_FN_NAME(assert)(st);
state.stack_pop();
return 0;
LUA_END

LUA_BEGIN(print)
int nargs = state.stack_size();
for(int i = 1; i <= nargs; i++)
{
std::string msg = state.stack_get_string(i);
tz::dbgui::add_to_lua_log(msg);
}
tz::dbgui::add_to_lua_log("\n");
state.stack_pop(nargs);
return 0;
LUA_END

void api_initialise(state& s)
{
s.assign_emptytable("tz");
s.assign_func("tz.assert", LUA_FN_NAME(assert));
s.assign_func("tz.error", LUA_FN_NAME(error));
LUA_REGISTER_ONE(print, s);
s.assign_emptytable("tz.version");
s.assign_emptytable("thread");

std::ostringstream sstr;
sstr << std::this_thread::get_id();
s.assign_string("thread.id", sstr.str());
s.assign_string("LUA_PATH", std::filesystem::current_path().generic_string());

tz::version ver = tz::get_version();

s.assign_uint("tz.version.major", ver.major);
s.assign_uint("tz.version.minor", ver.minor);
s.assign_uint("tz.version.patch", ver.patch);
s.assign_string("tz.version.string", ver.to_string());
}
}
15 changes: 12 additions & 3 deletions src/tz/lua/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@
#include "tz/lua/state.hpp"
#include "tz/core/debug.hpp"

#define LUA_BEGIN(name) int luafn_##name(void* state){

// `s` == underlying lua_State*. `state` == tz::lua::state
#define LUA_BEGIN(name) int luafn_##name(void* s){tz::lua::state state{s};

#define LUA_END }
#define LUA_REGISTER(name) tz::lua::for_all_states([](tz::lua::state& s){s.assign_func(#name, luafn_##name);});
#define LUA_REGISTER_ALL(name) tz::lua::for_all_states([](tz::lua::state& s){s.assign_func(#name, luafn_##name);});
#define LUA_REGISTER_ONE(name, state) s.assign_func(#name, luafn_##name)
#define LUA_FN_NAME(name) luafn_##name

namespace tz::lua
{
void api_initialise(state& s);
}

// example: define in a TU
//LUA_BEGIN(test_me_please)
Expand All @@ -15,7 +24,7 @@
//LUA_END

// usage: somewhere during runtime, invoke:
// LUA_REGISTER(test_me_please)
// LUA_REGISTER_ALL(test_me_please)
// which makes the function resident to all lua states on both the main thread and the job system worker threads.

#endif // TZ_LUA_API_HPP
154 changes: 84 additions & 70 deletions src/tz/lua/state.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#include "tz/lua/state.hpp"
#include "tz/lua/api.hpp"
#include "tz/core/debug.hpp"
#include "tz/core/data/version.hpp"
#include "tz/dbgui/dbgui.hpp"
#include "tz/core/job/job.hpp"
#include <sstream>
#include <iostream>
#include <sstream>
#include <cstdint>
#include <map>
#include <filesystem>
#include <mutex>

extern "C"
Expand Down Expand Up @@ -204,6 +203,83 @@ namespace tz::lua
return std::nullopt;
}

std::size_t state::stack_size() const
{
auto* s = static_cast<lua_State*>(this->lstate);
return lua_gettop(s);
}

void state::stack_pop(std::size_t count)
{
auto* s = static_cast<lua_State*>(this->lstate);
lua_pop(s, count);
}

bool state::stack_get_bool(std::size_t idx, bool type_check) const
{
auto* s = static_cast<lua_State*>(this->lstate);
if(lua_isboolean(s, idx) || !type_check)
{
return lua_toboolean(s, idx);
}
else
{
std::string stackdata = this->collect_stack();
tz::error("Lua stack entry %zu requested as `bool`, type error. Stack:\n%s", idx, stackdata.c_str());
return false;
}
}

double state::stack_get_double(std::size_t idx, bool type_check) const
{
auto* s = static_cast<lua_State*>(this->lstate);
if(lua_isnumber(s, idx) || !type_check)
{
return lua_tonumber(s, idx);
}
else
{
std::string stackdata = this->collect_stack();
tz::error("Lua stack entry %zu requested as `double/float`, type error. Stack:\n%s", idx, stackdata.c_str());
return false;
}
}

float state::stack_get_float(std::size_t idx, bool type_check) const
{
return static_cast<float>(this->stack_get_double(idx, type_check));
}

std::int64_t state::stack_get_int(std::size_t idx, bool type_check) const
{
return static_cast<std::int64_t>(this->stack_get_double(idx, type_check));
}

std::uint64_t state::stack_get_uint(std::size_t idx, bool type_check) const
{
return static_cast<std::uint64_t>(this->stack_get_double(idx, type_check));
}

std::string state::stack_get_string(std::size_t idx, bool type_check) const
{
auto* s = static_cast<lua_State*>(this->lstate);
// important note: lua under-the-hood does implicit conversions to-and-from
// string unless i explicitly disable them (e.g -DLUA_NOCVTN2S). this means
// that type check will always succeed for strings, even when they are not.
// however, i'm keeping the logic here still just for consistency.
if(lua_isstring(s, idx) || !type_check)
{
std::size_t len;
return {lua_tolstring(s, idx, &len), len};
}
else
{
std::string stackdata = this->collect_stack();
tz::error("Lua stack entry %zu requested as `string`, type error. Stack:\n%s", idx, stackdata.c_str());
return "";
}
}

std::string state::collect_stack() const
{
if(!this->impl_check_stack(3))
Expand Down Expand Up @@ -248,23 +324,20 @@ namespace tz::lua
return this->owner;
}

void* state::operator()() const
{
return this->lstate;
}

bool state::impl_check_stack(std::size_t sz) const
{
auto* s = static_cast<lua_State*>(this->lstate);
return lua_checkstack(s, sz);
}

void tz_inject_state(state& s);

thread_local state defstate = {};
std::mutex state_creation_mtx;

int morb(lua_State* state)
{
tz::report("its morbin' time!");
return 0;
}

state& get_state()
{
if(!defstate.valid())
Expand All @@ -275,7 +348,7 @@ namespace tz::lua
luaL_openlibs(l);
defstate = state{static_cast<void*>(l)};

tz_inject_state(defstate);
api_initialise(defstate);
}
return defstate;
}
Expand All @@ -300,63 +373,4 @@ namespace tz::lua
// remember also add it for main thread (which is us).
fn(get_state());
}

int tz_lua_assert(lua_State* state)
{
bool b = lua_toboolean(state, 1);
std::string stack = lua::state{state}.collect_stack();
lua_Debug ar;
lua_getstack(state, 1, &ar);
lua_getinfo(state, "nSl", &ar);
if(!b && TZ_DEBUG)
{
tz::dbgui::add_to_lua_log("<<Lua Assert Failure Detected>>");
}
tz::assert(b, "Lua Assertion Failure: ```lua\n\n%s\n\n```\nOn line %d\nStack:\n%s", ar.source, ar.currentline, stack.c_str());
return 0;
}

int tz_lua_error(lua_State* state)
{
lua_pushboolean(state, false);
return 1 + tz_lua_assert(state);
}

int tz_print(lua_State* state)
{
int nargs = lua_gettop(state);
for(int i = 1; i <= nargs; i++)
{
const char* msg = luaL_tolstring(state, i, nullptr);
if(msg != nullptr)
{
tz::dbgui::add_to_lua_log(msg);
}
}
tz::dbgui::add_to_lua_log("\n");
lua_pop(state, nargs);
return 0;
}

void tz_inject_state(state& s)
{
s.assign_emptytable("tz");
s.assign_emptytable("tz.version");
s.assign_func("tz.assert", tz_lua_assert);
s.assign_func("tz.error", tz_lua_error);
s.assign_func("print", tz_print);

std::ostringstream sstr;
sstr << std::this_thread::get_id();
s.assign_string("tz.thread", sstr.str());

s.assign_string("LUA_PATH", std::filesystem::current_path().generic_string());

tz::version ver = tz::get_version();

s.assign_uint("tz.version.major", ver.major);
s.assign_uint("tz.version.minor", ver.minor);
s.assign_uint("tz.version.patch", ver.patch);
s.assign_string("tz.version.string", ver.to_string());
}
}
9 changes: 9 additions & 0 deletions src/tz/lua/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,18 @@ namespace tz::lua
std::optional<double> get_double(const char* varname) const;
std::optional<std::int64_t> get_int(const char* varname) const;
std::optional<std::uint64_t> get_uint(const char* varname) const;
std::size_t stack_size() const;
void stack_pop(std::size_t count = 1);
bool stack_get_bool(std::size_t idx, bool type_check = true) const;
double stack_get_double(std::size_t idx, bool type_check = true) const;
float stack_get_float(std::size_t idx, bool type_check = true) const;
std::int64_t stack_get_int(std::size_t idx, bool type_check = true) const;
std::uint64_t stack_get_uint(std::size_t idx, bool type_check = true) const;
std::string stack_get_string(std::size_t idx, bool type_check = true) const;
std::string collect_stack() const;
const std::string& get_last_error() const;
std::thread::id get_owner_thread_id() const;
void* operator()() const;
private:
bool impl_check_stack(std::size_t sz) const;
mutable std::string last_error = "";
Expand Down

0 comments on commit 30b3c48

Please sign in to comment.