Last active
June 16, 2023 15:46
-
-
Save alex-ac/4514013 to your computer and use it in GitHub Desktop.
Lua C++ wrapper. C++11 templates street magic.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Lua.hh> | |
extern "C" { | |
#include <lualib.h> | |
#include <lauxlib.h> | |
}; | |
using namespace util; | |
int Lua::call(lua_State *vm) { | |
auto function = (std::function<int(Lua&)> *) | |
lua_touserdata(vm, lua_upvalueindex(1)); | |
auto l = Lua(vm); | |
return (*function)(l); | |
} | |
Lua::Lua(lua_State *vm): | |
del(false), | |
vm(vm) | |
{} | |
Lua::Lua(): | |
del(true), | |
vm(luaL_newstate()) | |
{ | |
luaL_openlibs(vm); | |
} | |
Lua::~Lua() { | |
for (auto lambda : lambdas) | |
delete lambda; | |
if (del) | |
lua_close(vm); | |
} | |
void Lua::lambda(std::function<int(Lua&)> *function, const std::string& name) { | |
lambdas.push_back(function); | |
userdata(function); | |
closure(Lua::call); | |
save(name); | |
} | |
void Lua::file(const std::string& name) { | |
luaL_dofile(vm, name.c_str()) && lua_error(vm); | |
} | |
void Lua::load(const std::string& name, int i) { | |
int t = i; | |
if (lua_gettop(vm) - (i>0?i:-i) < 0 || !lua_istable(vm, i)) { | |
if (-1 == i) | |
t = LUA_GLOBALSINDEX; | |
else | |
luaL_error(vm, "Invalid load operation (out of stack)!"); | |
} | |
lua_getfield(vm, t, name.c_str()); | |
} | |
void Lua::pop(const int i) { | |
if (lua_gettop(vm) - i < 0) | |
luaL_error(vm, "Invalid pop operation (out of stack)!"); | |
lua_pop(vm, i); | |
} | |
void Lua::remove(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid remove operation (out of stack)!"); | |
lua_remove(vm, i); | |
} | |
void Lua::table() { | |
lua_newtable(vm); | |
} | |
void Lua::metatable(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0 || !lua_istable(vm, i)) | |
luaL_error(vm, "Invalid set metatable operation (out of stack)!"); | |
lua_setmetatable(vm, i); | |
} | |
void Lua::closure(int (*callback)(lua_State *), const int i) { | |
if (lua_gettop(vm) - i < 0) | |
luaL_error(vm, "Invalid closure operation (out of stack)!"); | |
lua_pushcclosure(vm, callback, i); | |
} | |
void Lua::copy(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid copy operation (out of stack)!"); | |
lua_pushvalue(vm, i); | |
} | |
void Lua::save(const std::string& name, const int i) { | |
int t = i; | |
if (lua_gettop(vm) - (i>0?i:-i) < 0 || !lua_istable(vm, i)) { | |
if (-2 == i) | |
t = LUA_GLOBALSINDEX; | |
else | |
luaL_error(vm, "Invalid save operation (out of stack)!"); | |
} | |
lua_setfield(vm, t, name.c_str()); | |
} | |
bool Lua::is_nil(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid is nil operation (out of stack)!"); | |
return lua_isnil(vm, i); | |
} | |
void Lua::object(const LuaClass *object, const std::string& name) { | |
load(name); | |
table(); | |
load("mtab", -2); | |
metatable(); | |
remove(-2); | |
userdata(object); | |
save("__self__"); | |
} | |
LuaClass * Lua::object(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid object operation (out of stack)!"); | |
if (!lua_istable(vm, i)) | |
luaL_error(vm, "Invalid object (table expected)!"); | |
load("__self__", i); | |
auto ret = (LuaClass *)userdata(); | |
pop(); | |
return ret; | |
} | |
void Lua::userdata(const void *d) { | |
lua_pushlightuserdata(vm, const_cast<void *>(d)); | |
} | |
void * Lua::userdata(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid userdata operation (out of stack)! %d, %d", lua_gettop(vm), i); | |
if (!lua_isuserdata(vm, i)) | |
luaL_error(vm, "Invalid userdata operation (userdata expected)!"); | |
return lua_touserdata(vm, i); | |
} | |
void Lua::number(const lua_Number n) { | |
lua_pushnumber(vm, n); | |
} | |
lua_Number Lua::tonumber(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid tonumber operation (out of stack)!"); | |
if (!lua_isnumber(vm, i)) | |
luaL_error(vm, "Invalid tonumber operation (number expected)!"); | |
return lua_tonumber(vm, i); | |
} | |
void Lua::string(const std::string& string) { | |
lua_pushstring(vm, string.c_str()); | |
} | |
std::string Lua::string(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid string operation (out of stack)!"); | |
if (!lua_isstring(vm, i)) | |
luaL_error(vm, "Invalid string operation (string expected)!"); | |
return lua_tostring(vm, i); | |
} | |
void Lua::boolean(const bool b) { | |
lua_pushboolean(vm, b); | |
} | |
bool Lua::boolean(const int i) { | |
if (lua_gettop(vm) - (i>0?i:-i) < 0) | |
luaL_error(vm, "Invalid boolean operation (out of stack)!"); | |
if (!lua_isboolean(vm, i)) | |
luaL_error(vm, "Invalid boolean operation (boolean expected)!"); | |
return lua_toboolean(vm, i); | |
} | |
void Lua::LuaObject::export_class(Lua& vm) { | |
} | |
void Lua::LuaObject::export_me(Lua& vm) { | |
vm.export_class<LuaObject>(); | |
} | |
const std::string Lua::LuaObject::class_name() { | |
return "Object"; | |
} | |
const std::string Lua::LuaObject::obj_class_name() const { | |
return "Object"; | |
} | |
void Lua::export_function(const std::string& name, void (*callback)()) { | |
auto function = new std::function<int(Lua&)>([callback] (Lua& vm) -> int { | |
(*callback)(); | |
return 0; | |
}); | |
lambda(function, name); | |
} | |
template <> | |
int Lua::ret<lua_Number>(const lua_Number r) { | |
number(r); | |
return 1; | |
} | |
template <> | |
int Lua::ret<std::string>(const std::string r) { | |
string(r); | |
return 1; | |
} | |
template <> | |
int Lua::ret<bool>(const bool r) { | |
boolean(r); | |
return 1; | |
} | |
template <> | |
int Lua::ret<int>(const int r) { | |
number(r); | |
return 1; | |
} | |
template <> | |
std::string Lua::arg<std::string>(const int i) { | |
return string(i); | |
} | |
template <> | |
lua_Number Lua::arg<lua_Number>(const int i) { | |
return tonumber(i); | |
} | |
template <> | |
bool Lua::arg<bool>(const int i) { | |
return boolean(i); | |
} | |
template <> | |
lua_Integer Lua::arg<lua_Integer>(const int i) { | |
return tonumber(i); | |
} | |
template <> | |
int Lua::arg<int>(const int i) { | |
return tonumber(i); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <string> | |
#include <vector> | |
#include <functional> | |
#include <tuple> | |
#include <type_traits> | |
#include <iostream> | |
extern "C" { | |
#include <lua.h> | |
}; | |
namespace util { | |
class LuaClass { | |
private: | |
protected: | |
public: | |
virtual const std::string obj_class_name() const = 0; | |
}; | |
class Lua { | |
protected: | |
template <typename T> | |
int ret(const T r) { | |
static_assert(std::is_convertible<T, LuaClass*>::value, | |
"LuaClass * required!"); | |
object((LuaClass *)r, std::remove_pointer<T>::type::class_name()); | |
return 1; | |
} | |
template <class T> | |
T arg(const int i) { | |
if (std::is_base_of<util::LuaClass, T>::value) { | |
return *(T *)object(i); | |
} else { | |
return *(T *)userdata(i); | |
} | |
} | |
template <class T> | |
T* argp(const int i) { | |
if (std::is_base_of<util::LuaClass, T>::value) { | |
return (T *)object(i); | |
} else { | |
return (T *)userdata(i); | |
} | |
} | |
template <typename T, typename T1, typename... Args> | |
std::tuple<T, T1, Args...> args(const int i = 1) { | |
T t = arg<T>( i); | |
return std::tuple_cat(t, args<T1, Args...>( i + 1)); | |
} | |
template <typename T> | |
std::tuple<T> args(const int i = 1) { | |
return std::tuple<T>(arg<T>( i)); | |
} | |
template <typename... Args> struct sizer { | |
static const int size = sizeof...(Args); | |
}; | |
template <int N> struct apply_method { | |
template <class T, typename R, typename... MethodArgs, | |
typename... TupleArgs, typename... Args> | |
static R apply(T* o, R (T::*method)(MethodArgs...), | |
std::tuple<TupleArgs...>& t, Args... args) { | |
return | |
apply_method<N-1>::apply(o, method, t, std::get<N-1>(t), args...); | |
} | |
}; | |
template <int N> struct apply_function { | |
template <typename R, typename... FunctionArgs, typename... TupleArgs, | |
typename... Args> | |
static R apply(R (*function)(FunctionArgs...), | |
std::tuple<TupleArgs...>& t, Args... args) { | |
return | |
apply_function<N-1>::apply(function, t, std::get<N-1>(t), args...); | |
} | |
}; | |
private: | |
bool del; | |
lua_State * vm; | |
std::vector<std::function<int(Lua&)> *> lambdas; | |
static int call(lua_State *vm); | |
public: | |
Lua(lua_State *vm); | |
Lua(); | |
~Lua(); | |
void lambda(std::function<int(Lua&)> *function, const std::string& name); | |
void load(const std::string& name, const int i = -1); | |
void pop(const int i = 1); | |
void remove(const int i); | |
void table(); | |
void metatable(const int i = -2); | |
void closure(int (*)(lua_State *), const int i = 1); | |
void copy(const int i = -1); | |
void save(const std::string& name, const int i = -2); | |
void file(const std::string& name); | |
bool is_nil(const int i = -1); | |
void object(const LuaClass *, const std::string& name); | |
LuaClass * object(const int i = -1); | |
void userdata(const void *); | |
void * userdata(const int i = -1); | |
void number(const lua_Number); | |
lua_Number tonumber(const int i = -1); | |
void string(const std::string&); | |
std::string string(const int i = -1); | |
void boolean(const bool); | |
bool boolean(const int i = -1); | |
class LuaObject : public LuaClass { | |
private: | |
protected: | |
public: | |
static void export_class(Lua& vm); | |
static void export_me(Lua& vm); | |
static const std::string class_name(); | |
virtual const std::string obj_class_name() const override; | |
}; | |
/* | |
* class LuaObject: public LuaClass {}; | |
* class P : public LuaClass {}; | |
* class T : public LuaClass {}; | |
* | |
* -> T::export_me(; | |
* -> export_class<T, P>(); | |
* -> T::class_name(); | |
* -> P::class_name(); | |
* -> P::export_me(; | |
* -> export_class<P>(); | |
* -> P::class_name(); | |
* -> LuaObject::class_name(); | |
* -> LuaObject::export_me(; | |
* -> export_class<LuaObject>(); | |
* -> LuaObject::class_name(); | |
* -> LuaObject::class_name(); | |
* -> LuaObject::export_class(; | |
* -> P::export_class(; | |
* -> T::export_class(; | |
*/ | |
template<class T, class P = LuaObject> | |
void export_class() { | |
static_assert(std::is_base_of<util::LuaClass, T>::value, | |
"LuaClass implementation expected!"); | |
auto name = T::class_name(); | |
auto parent_name = P::class_name(); | |
bool parent = name != parent_name; | |
if (parent) { | |
P::export_me(*this); | |
} | |
load(name); | |
if (!is_nil()) { | |
pop(); | |
return; | |
} | |
pop(); | |
table(); | |
table(); | |
copy(-2); | |
save("__index"); | |
save("mtab"); | |
if (parent) { | |
load(parent_name); | |
load("mtab"); | |
metatable(-3); | |
pop(); | |
} | |
T::export_class(*this); | |
save(name); | |
} | |
template <typename R, class T, typename... Args> | |
void export_method(const std::string& name, | |
R (T::*method)(Args...)) { | |
auto function = new std::function<int(Lua&)>([method] (Lua& vm) -> int { | |
auto tuple = vm.args<Args...>( 2); | |
return vm.ret( | |
apply_method<std::tuple_size<decltype(tuple)>::value> | |
::apply(vm.argp<T>( 1), method, tuple)); | |
}); | |
lambda(function, name); | |
} | |
template <class T, typename... Args> | |
void export_method(const std::string& name, | |
void (T::*method)(Args...)) { | |
auto function = new std::function<int(Lua&)>([method] (Lua& vm) -> int { | |
auto tuple = vm.args<Args...>( 2); | |
apply_method<std::tuple_size<decltype(tuple)>::value> | |
::apply(vm.argp<T>( 1), method, tuple); | |
return 0; | |
}); | |
lambda(function, name); | |
} | |
template <typename R, class T> | |
void export_method(const std::string& name, R (T::*method)()) { | |
auto function = new std::function<int(Lua&)>([method] (Lua& vm) -> int { | |
return vm.ret( (vm.argp<T>( 1)->*method)()); | |
}); | |
lambda(function, name); | |
} | |
template <class T> | |
void export_method(const std::string& name, void (T::*method)()) { | |
auto function = new std::function<int(Lua&)>([method] (Lua& vm) -> int { | |
(vm.argp<T>( 1)->*method)(); | |
return 0; | |
}); | |
lambda(function, name); | |
} | |
template <typename R, typename... Args> | |
void export_function(const std::string& name, | |
R (*callback)(Args...)) { | |
auto function = new std::function<int(Lua&)>([callback] (Lua& vm) -> int { | |
auto tuple = vm.args<Args...>(); | |
return vm.ret( | |
apply_function<std::tuple_size<decltype(tuple)>::value> | |
::apply(callback, tuple)); | |
}); | |
lambda(function, name); | |
} | |
template <typename... Args> | |
void export_function(const std::string& name, | |
void (*callback)(Args...)) { | |
auto function = new std::function<int(Lua&)>([callback] (Lua& vm) -> int { | |
auto tuple = vm.args<Args...>(); | |
apply_function<std::tuple_size<decltype(tuple)>::value> | |
::apply(callback, tuple); | |
return 0; | |
}); | |
lambda(function, name); | |
} | |
template <typename R> | |
void export_function(const std::string& name, R (*callback)()) { | |
auto function = new std::function<int(Lua&)>([callback] (Lua& vm) -> int { | |
return vm.ret( (*callback)()); | |
}); | |
lambda(function, name); | |
} | |
void export_function(const std::string& name, void (*callback)()); | |
}; | |
template <> | |
int Lua::ret<lua_Number>(const lua_Number r); | |
template <> | |
int Lua::ret<std::string>(const std::string r); | |
template <> | |
int Lua::ret<bool>(const bool r); | |
template <> | |
int Lua::ret<int>(const int r); | |
template <> | |
std::string Lua::arg<std::string>(const int i); | |
template <> | |
lua_Number Lua::arg<lua_Number>(const int i); | |
template <> | |
lua_Integer Lua::arg<lua_Integer>(const int i); | |
template <> | |
int Lua::arg<int>(const int i); | |
template <> | |
bool Lua::arg<bool>(const int i); | |
template <> struct Lua::apply_method<0> { | |
template <class T, typename R, typename... MethodArgs, | |
typename... TupleArgs, typename... Args> | |
static R apply(T* o, R (T::*method)(MethodArgs...), | |
std::tuple<TupleArgs...>& t, Args... args) { | |
return (o->*method)(args...); | |
} | |
}; | |
template <> struct Lua::apply_function<0> { | |
template <typename R, typename... FunctionArgs, typename... TupleArgs, | |
typename... Args> | |
static R apply(R (*function)(FunctionArgs...), | |
std::tuple<TupleArgs...>& t, Args... args) { | |
return | |
(*function)(args...); | |
} | |
}; | |
} // namespace util; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <Lua.hh> | |
void test() { | |
std::cout << "Hello, world! " << std::endl; | |
} | |
void test1(int a) { | |
std::cout << a << std::endl; | |
} | |
int test2() { | |
return 2; | |
} | |
int test3(int a) { | |
return a + 1; | |
} | |
class test_class : public util::LuaClass { | |
public: | |
static test_class *construct() { | |
return new test_class(); | |
} | |
void deconstruct() { | |
delete this; | |
} | |
static void export_me(util::Lua& vm) { | |
vm.export_class<test_class>(); | |
} | |
static void export_class(util::Lua& vm) { | |
vm.export_function("new", &test_class::construct); | |
vm.export_method("delete", &test_class::deconstruct); | |
vm.export_method("test", &test_class::test); | |
vm.export_method("test1", &test_class::test1); | |
vm.export_method("test2", &test_class::test2); | |
vm.export_method("test3", &test_class::test3); | |
} | |
static const std::string class_name() { | |
return "test_class"; | |
} | |
virtual const std::string obj_class_name() const override { | |
return "test_class"; | |
} | |
void test() { | |
std::cout << "Hello, world [test_class]" << std::endl; | |
} | |
int test1() { | |
return 1; | |
} | |
int test2(int a) { | |
return a*2; | |
} | |
void test3(int a) { | |
std::cout << a << std::endl; | |
} | |
}; | |
int main(int argc, char *argv[]) { | |
util::Lua l; | |
l.export_function("test", &test); | |
l.export_function("test1", &test1); | |
l.export_function("test2", &test2); | |
l.export_function("test3", &test3); | |
test_class::export_me(l); | |
l.file("test.lua"); | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test() | |
test1(1) | |
print(test2()) | |
print(test3(3)) | |
t = test_class.new() | |
print(t) | |
t:test() | |
print(t:test1()) | |
print(t:test2(2)) | |
t:test3(3) | |
t:delete() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment