Sélectionner une révision Git
-
Alexandre Morignot a rédigéAlexandre Morignot a rédigé
CRTPLuaScriptObject.hh 9,78 Kio
#pragma once
#include "LuaContext.hh"
#include "ScriptOption.hh"
#include "lua.hpp"
#include <vector>
#include <cstdio>
namespace Vivy::Script
{
// clang-format off
#define LUA_DECL_METHOD(class, name) luaL_Reg{ #name, class ::name }
#define LUA_DECL_META_METHOD(class, meta, name) luaL_Reg{ meta, class ::name }
#define LUA_DECL_CREATE(class) luaL_Reg{ "new", class ::CREATE }
#define LUA_RETURN_NOTHING(L) { lua_settop(L, 0); return 0; }
// clang-format on
#define script_class(theClassName) \
class theClassName final : public CRTPLuaScriptObject<theClassName>
#define method_list static constexpr inline auto
#define LUA_SCRIPTABLE_CLASS(theClassName) \
VIVY_UNMOVABLE_OBJECT(theClassName) \
static constexpr char className[] = #theClassName; \
friend CRTPLuaScriptObject<theClassName>; \
theClassName(lua_State *L) noexcept \
{ \
err(LuaContext::getContext(L)) \
<< "Create instance of " << theClassName::className << "\n"; \
} \
~theClassName() noexcept {}
// CRTP to expose objects to Lua
template <class Object> class CRTPLuaScriptObject {
protected:
static void IterateOverArray(lua_State *const L, const int tblIndex,
const auto callback) noexcept
{
luaL_checktype(L, tblIndex, LUA_TTABLE);
const std::size_t count = lua_rawlen(L, tblIndex);
if (count >= std::numeric_limits<int>::max())
luaL_error(L, "To many items (%lu) in table to iterate over!", count);
const int countInt = static_cast<const int>(count);
for (int i = 1; i <= countInt; ++i) {
luaL_checktype(L, tblIndex, LUA_TTABLE);
lua_pushinteger(L, i); // i -> top stack
lua_gettable(L, tblIndex); // T[i] -> top stack and pop i
if (lua_type(L, -1) == LUA_TNIL)
break; // Sentinel check
// If the callback didn't pop the object form the stack, pop
// it ourself. The value will be accessible at the top of the
// stack (index -1).
if (!callback())
lua_pop(L, -1);
}
}
static int GC(lua_State *const L) noexcept
{
const Object *const obj = reinterpret_cast<const Object *>(lua_topointer(L, 1));
auto *context = LuaContext::getContext(L);
err(context) << "Call destructor for " << Object::className << "\n";
obj->~Object();
return 0;
}
static int TOSTRING(lua_State *const L) noexcept
{
lua_pushfstring(L, "%s { }", Object::className);
return 1;
}
static int CREATE(lua_State *const L) noexcept
{
lua_settop(L, 0);
Object *const obj = reinterpret_cast<Object *>(lua_newuserdata(L, sizeof(Object)));
new (obj) Object(L);
luaL_getmetatable(L, Object::className);
lua_setmetatable(L, 1);
return 1;
}
static const std::string_view CHECK_STRING_VIEW(lua_State *const L, int index) noexcept
{
std::size_t len = 0;
const char *const str = luaL_checklstring(L, index, &len);
return std::string_view(str, len);
}
static inline constexpr luaL_Reg luaRegDefaultGC = { "__gc", GC };
static inline constexpr luaL_Reg luaRegDefaultTOSTRING = { "__tostring", TOSTRING };
public:
static Object *CHECK(lua_State *const L, const int index) noexcept
{
return reinterpret_cast<Object *>(luaL_checkudata(L, index, Object::className));
}
static void Register(lua_State *const L) noexcept
{
// Fill the method table, newclass = {}
lua_newtable(L);
const int methodtable = lua_gettop(L);
// Fill the meta-table, metatable = {}
{
luaL_newmetatable(L, Object::className);
const int metatable = lua_gettop(L);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable);
lua_settable(L, metatable);
lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable);
lua_settable(L, metatable);
for (const luaL_Reg &meta : Object::metaMethods) {
lua_pushstring(L, meta.name);
lua_pushcfunction(L, meta.func);
lua_settable(L, metatable);
}
lua_pop(L, 1);
}
// Fill the method table with specified methods
for (const luaL_Reg &method : Object::methods) {
lua_pushstring(L, method.name);
lua_pushcfunction(L, method.func);
lua_settable(L, methodtable);
}
// Last thing to do it seems...
lua_pop(L, 1); // drop method-table
lua_register(L, Object::className, CREATE); // Register the create method
}
private:
// The user value count
int userValueCount{ 0 };
protected:
// Add a user value to this object. Do not used lua_setuservalue or
// lua_setiuservalue directly, use this proxy method.
void addUserValue(lua_State *const L, const int index) noexcept
{
lua_setiuservalue(L, index, ++userValueCount);
}
};
// Option declaration
script_class (OptionDeclaration) {
LUA_SCRIPTABLE_CLASS(OptionDeclaration)
static int addOptionContent(lua_State *const) noexcept;
static int TOSTRING(lua_State *const) noexcept;
method_list metaMethods = {
luaRegDefaultGC,
LUA_DECL_META_METHOD(OptionDeclaration, "__tostring", TOSTRING),
};
method_list methods = { LUA_DECL_METHOD(OptionDeclaration, addOptionContent),
LUA_DECL_CREATE(OptionDeclaration) };
public:
// Different option types
std::unordered_map<std::string, ScriptOption> options;
};
// Job declaration
script_class (JobDeclaration) {
LUA_SCRIPTABLE_CLASS(JobDeclaration)
static int setName(lua_State *) noexcept;
static int setOptions(lua_State *) noexcept;
static int setImportFromScript(lua_State *) noexcept;
static int TOSTRING(lua_State *const) noexcept;
method_list metaMethods = { luaRegDefaultGC,
LUA_DECL_META_METHOD(JobDeclaration, "__tostring", TOSTRING) };
method_list methods = { LUA_DECL_METHOD(JobDeclaration, setName),
LUA_DECL_METHOD(JobDeclaration, setOptions),
LUA_DECL_METHOD(JobDeclaration, setImportFromScript),
LUA_DECL_CREATE(JobDeclaration) };
public:
std::string name{};
std::string parentScript{};
std::vector<OptionDeclaration *> options{};
};
// Function declaration
script_class (FunctionDeclaration) {
LUA_SCRIPTABLE_CLASS(FunctionDeclaration)
static int setName(lua_State *) noexcept;
static int setImportFromScript(lua_State *) noexcept;
static int TOSTRING(lua_State *const) noexcept;
method_list metaMethods = { luaRegDefaultGC,
LUA_DECL_META_METHOD(FunctionDeclaration, "__tostring", TOSTRING) };
method_list methods = { LUA_DECL_METHOD(FunctionDeclaration, setName),
LUA_DECL_METHOD(FunctionDeclaration, setImportFromScript),
LUA_DECL_CREATE(FunctionDeclaration) };
public:
std::string name{};
std::string parentScript{};
};
// Module declaration
script_class (ModuleDeclaration) {
LUA_SCRIPTABLE_CLASS(ModuleDeclaration)
static int setName(lua_State *const) noexcept;
static int setAuthorName(lua_State *const) noexcept;
static int setRevision(lua_State *const) noexcept;
static int setDescription(lua_State *const) noexcept;
static int setImplementationFile(lua_State *const) noexcept;
static int setImports(lua_State *const) noexcept;
static int setOptions(lua_State *const) noexcept;
static int setFunctions(lua_State *const) noexcept;
static int setJobs(lua_State *const) noexcept;
static int pushToRuntime(lua_State *const) noexcept;
method_list metaMethods = { luaRegDefaultGC };
method_list methods = {
LUA_DECL_METHOD(ModuleDeclaration, setName),
LUA_DECL_METHOD(ModuleDeclaration, setDescription),
LUA_DECL_METHOD(ModuleDeclaration, setRevision),
LUA_DECL_METHOD(ModuleDeclaration, setAuthorName),
LUA_DECL_METHOD(ModuleDeclaration, setImplementationFile),
LUA_DECL_METHOD(ModuleDeclaration, setImports),
LUA_DECL_METHOD(ModuleDeclaration, setOptions),
LUA_DECL_METHOD(ModuleDeclaration, setFunctions),
LUA_DECL_METHOD(ModuleDeclaration, setJobs),
LUA_DECL_METHOD(ModuleDeclaration, pushToRuntime),
LUA_DECL_CREATE(ModuleDeclaration),
};
// Will be resolved later
std::vector<std::string> importNames{};
std::vector<const ModuleDeclaration *> importedModules{};
bool validateModule(lua_State *const) const noexcept;
public:
std::string moduleName{};
std::string authorName{};
std::string revision{};
std::string moduleDescription{};
std::string implementationFile{};
std::vector<OptionDeclaration *> moduleOptions{};
std::vector<FunctionDeclaration *> moduleFunctions{};
std::vector<JobDeclaration *> moduleJobs{};
bool resolvModules(LuaContext *const) noexcept;
};
// Holds all the free functions (well, not really free functions here...)
script_class (FreeFunctions) {
LUA_SCRIPTABLE_CLASS(FreeFunctions)
static int print(lua_State *) noexcept;
method_list metaMethods = { luaRegDefaultGC };
method_list methods = { LUA_DECL_METHOD(FreeFunctions, print), LUA_DECL_CREATE(FreeFunctions) };
};
}