lcpu: Cleanup, and begin the implementation of a new system for Lua binding
I didn't like how I did it before, and I don't like how right now everything binds a bit differently. So let's unify it using the way I like (from the LuaDevice binding)!
This commit is contained in:
parent
40c539bf22
commit
1b347eecc8
|
@ -10,6 +10,22 @@ if SERVER then
|
|||
return
|
||||
end
|
||||
|
||||
----[[
|
||||
testobj = LCPUNative.CreateTest()
|
||||
print(testobj:Test())
|
||||
print(testobj.Variable)
|
||||
|
||||
testobj.MemberVariable = 32.9
|
||||
print(testobj.MemberVariable)
|
||||
|
||||
testobj.Variable = 32.1 -- this should fial
|
||||
testobj.Test = nil;
|
||||
|
||||
print(testobj.Variable)
|
||||
|
||||
print(testobj.Name)
|
||||
--]]
|
||||
|
||||
-- rapid iteration requires rapid solutions
|
||||
--[[
|
||||
device = LCPUNative.CreateDevice(0x100000f0, 0x10)
|
||||
|
|
|
@ -1,10 +1,43 @@
|
|||
#include "LcpuGlobals.hpp"
|
||||
|
||||
#include "GarrysMod/Lua/Interface.h"
|
||||
#include "GarrysMod/Lua/LuaBase.h"
|
||||
#include "LuaCpu.hpp"
|
||||
#include "LuaDevice.hpp"
|
||||
#include "LuaHelpers.hpp"
|
||||
|
||||
/// test for the "new" lua object system
|
||||
struct TestLuaObject : public lcpu::lua::LuaObject<TestLuaObject> {
|
||||
static const char* Name() { return "TestLuaObject"; }
|
||||
|
||||
static void RegisterClass(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
RegisterClassStart(LUA);
|
||||
// Metamethods can be registered here; in this case, our test object doesn't need any
|
||||
RegisterClassEnd(LUA);
|
||||
|
||||
// Register methods. Maybe later I'll do some crazy template stuff; for now this is pretty barebones.
|
||||
RegisterMethod("Test", &Test);
|
||||
RegisterGetter("Variable", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushNumber(32.6); });
|
||||
RegisterGetter("MemberVariable", [](GarrysMod::Lua::ILuaBase* LUA) {
|
||||
auto self = TestLuaObject::FromLua(LUA, 1);
|
||||
LUA->PushNumber(self->n);
|
||||
});
|
||||
RegisterSetter("MemberVariable", [](GarrysMod::Lua::ILuaBase* LUA) {
|
||||
// 3 will be the assignment stack position
|
||||
auto self = TestLuaObject::FromLua(LUA, 1);
|
||||
self->n = LUA->GetNumber(3);
|
||||
});
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION(Test)
|
||||
double n;
|
||||
};
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(TestLuaObject, Test) {
|
||||
LUA->PushString("hi :)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
||||
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
||||
auto memorySize = static_cast<u32>(LUA->GetNumber(1));
|
||||
|
@ -20,15 +53,23 @@ LUA_FUNCTION(LCPUNative_CreateCPU) {
|
|||
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
||||
auto base = LUA->CheckNumber(1);
|
||||
auto size = LUA->CheckNumber(2);
|
||||
lucore::LogInfo("Creating Lua device object mapped @ 0x{:08x} with size 0x{:08x}", static_cast<riscv::Address>(base), static_cast<riscv::Address>(size));
|
||||
lucore::LogInfo("Creating Lua device object mapped @ 0x{:08x} with size 0x{:08x}", static_cast<riscv::Address>(base),
|
||||
static_cast<riscv::Address>(size));
|
||||
LuaDevice::Create(LUA, static_cast<riscv::Address>(base), static_cast<riscv::Address>(size));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUA_FUNCTION(LCPUNative_CreateTest) {
|
||||
TestLuaObject::Create(LUA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
LuaCpu::Bind(LUA);
|
||||
LuaDevice::Bind(LUA);
|
||||
|
||||
TestLuaObject::RegisterClass(LUA);
|
||||
|
||||
// clang-format off
|
||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
||||
LUA->CreateTable();
|
||||
|
@ -37,6 +78,8 @@ void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
|||
|
||||
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateCPU, "CreateCPU");
|
||||
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateDevice, "CreateDevice");
|
||||
|
||||
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateTest, "CreateTest");
|
||||
LUA->SetField(-2, "LCPUNative");
|
||||
LUA->Pop();
|
||||
// clang-format on
|
||||
|
|
|
@ -9,6 +9,7 @@ bool LuaDevice::Clocked() const {
|
|||
}
|
||||
|
||||
void LuaDevice::Clock() {
|
||||
// clang-format off
|
||||
LuaState->ReferencePush(tableReference);
|
||||
LuaState->GetField(-1,"Clock");
|
||||
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
|
@ -18,6 +19,7 @@ void LuaDevice::Clock() {
|
|||
LuaState->Pop(); // pop the Clock function off the stack
|
||||
}
|
||||
LuaState->Pop(); // pop the reference
|
||||
// clang-format off
|
||||
}
|
||||
|
||||
riscv::Address LuaDevice::Base() const {
|
||||
|
@ -29,15 +31,7 @@ riscv::Address LuaDevice::Size() const {
|
|||
}
|
||||
|
||||
u32 LuaDevice::Peek(riscv::Address address) {
|
||||
/*if(peekHandlerReference != -1) {
|
||||
LuaState->ReferencePush(resetHandlerReference);
|
||||
LuaState->PushNumber(static_cast<double>(address));
|
||||
LuaState->Call(1, 1);
|
||||
auto result = LuaState->GetNumber(-1);
|
||||
LuaState->Pop();
|
||||
return static_cast<u32>(result);
|
||||
}*/
|
||||
|
||||
// clang-format off
|
||||
LuaState->ReferencePush(tableReference);
|
||||
LuaState->GetField(-1,"Peek");
|
||||
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
|
@ -52,17 +46,12 @@ u32 LuaDevice::Peek(riscv::Address address) {
|
|||
LuaState->Pop(); // pop whatever Peek is off the stack
|
||||
}
|
||||
LuaState->Pop(); // pop the table reference
|
||||
// clang-format on
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
void LuaDevice::Poke(riscv::Address address, u32 value) {
|
||||
/*if(pokeHandlerReference != -1) {
|
||||
LuaState->ReferencePush(pokeHandlerReference);
|
||||
LuaState->PushNumber(address);
|
||||
LuaState->PushNumber(value);
|
||||
LuaState->Call(2, 0);
|
||||
}*/
|
||||
|
||||
// clang-format off
|
||||
LuaState->ReferencePush(tableReference);
|
||||
LuaState->GetField(-1,"Poke");
|
||||
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
|
@ -74,14 +63,11 @@ void LuaDevice::Poke(riscv::Address address, u32 value) {
|
|||
LuaState->Pop(); // pop whatever Peek is
|
||||
}
|
||||
LuaState->Pop(); // pop the table reference
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void LuaDevice::Reset() {
|
||||
/*if(resetHandlerReference != -1) {
|
||||
LuaState->ReferencePush(resetHandlerReference);
|
||||
LuaState->Call(0, 0);
|
||||
}*/
|
||||
|
||||
// clang-format off
|
||||
LuaState->ReferencePush(tableReference);
|
||||
LuaState->GetField(-1,"Reset");
|
||||
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
|
@ -91,6 +77,7 @@ void LuaDevice::Reset() {
|
|||
LuaState->Pop(); // pop whatever reset is
|
||||
}
|
||||
LuaState->Pop(); // pop the reference
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
LuaDevice::LuaDevice(riscv::Address base, riscv::Address size) : base(base), size(size) {
|
||||
|
@ -146,8 +133,6 @@ void LuaDevice::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
|||
LUA_SET_C_FUNCTION(__newindex)
|
||||
LUA_CLASS_BIND_END();
|
||||
// clang-format on
|
||||
|
||||
|
||||
}
|
||||
|
||||
void LuaDevice::Create(GarrysMod::Lua::ILuaBase* LUA, riscv::Address base, riscv::Address size) {
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <GarrysMod/Lua/Interface.h>
|
||||
|
||||
#include <lucore/Logger.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "GarrysMod/Lua/LuaBase.h"
|
||||
|
||||
// These are like the official GMOD header LUA_FUNCTION but allow forward declaration
|
||||
// and implementation inside of classes, making writing class bindings that much less
|
||||
|
@ -49,7 +52,6 @@
|
|||
return 0; \
|
||||
}
|
||||
|
||||
|
||||
// Begin the Bind() method of a class. This just sets up boilerplate
|
||||
// and required things to setup a class.
|
||||
#define LUA_CLASS_BIND_BEGIN(T) \
|
||||
|
@ -75,6 +77,7 @@
|
|||
LUA->PushCFunction(name); \
|
||||
LUA->SetField(-2, altName);
|
||||
|
||||
namespace lcpu::lua {
|
||||
|
||||
inline std::string GetLuaString(GarrysMod::Lua::ILuaBase* LUA, int stackPos) {
|
||||
unsigned len {};
|
||||
|
@ -85,3 +88,176 @@ inline std::string GetLuaString(GarrysMod::Lua::ILuaBase* LUA, int stackPos) {
|
|||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template <class TImpl>
|
||||
struct LuaObject {
|
||||
using CFunc = GarrysMod::Lua::CFunc;
|
||||
using ILuaFunc = void (*)(GarrysMod::Lua::ILuaBase*);
|
||||
|
||||
/// Register a C++ method.
|
||||
static void RegisterMethod(const std::string& name, CFunc func) { methods()[name] = func; }
|
||||
|
||||
/// Register a getter for a value to be read.
|
||||
static void RegisterGetter(const std::string& name, ILuaFunc func) { getters()[name] = func; }
|
||||
|
||||
/// Register a setter. This can be used to make a
|
||||
/// C++ registered value read-write.
|
||||
static void RegisterSetter(const std::string& name, ILuaFunc func) { setters()[name] = func; }
|
||||
|
||||
// addl. arguments are forwarded to the C++ constructor
|
||||
template <class... Args>
|
||||
static void Create(GarrysMod::Lua::ILuaBase* LUA, Args&&... args) {
|
||||
auto ptr = new TImpl(static_cast<Args&&>(args)...);
|
||||
ptr->InitLuaStuff(LUA);
|
||||
LUA->PushUserType(ptr, __lua_typeid);
|
||||
}
|
||||
|
||||
static TImpl* FromLua(GarrysMod::Lua::ILuaBase* LUA, int stackPos) {
|
||||
LUA->CheckType(stackPos, __lua_typeid);
|
||||
return LUA->GetUserType<TImpl>(stackPos, __lua_typeid);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// This should be called first in your RegisterClass static method.
|
||||
/// This doesn't pop the metatable off so you can keep adding things to it
|
||||
static void RegisterClassStart(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
auto typeid_name = std::format("{}__typeid", TImpl::Name());
|
||||
|
||||
// clang-format off
|
||||
__lua_typeid = LUA->CreateMetaTable(TImpl::Name());
|
||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG);
|
||||
LUA->PushNumber(__lua_typeid);
|
||||
LUA->SetField(-2, typeid_name.c_str());
|
||||
LUA->Pop(); /* pop registry */
|
||||
LUA_SET_C_FUNCTION(__gc)
|
||||
LUA_SET_C_FUNCTION(__index)
|
||||
LUA_SET_C_FUNCTION(__newindex)
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/// Call in your RegisterClass() method when done registering metamethods.
|
||||
static void RegisterClassEnd(GarrysMod::Lua::ILuaBase* LUA) { LUA->Pop(); }
|
||||
|
||||
/// A detail function used to setup some stuff in the Create() method.
|
||||
void InitLuaStuff(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
lua = LUA;
|
||||
|
||||
// create the table used to store user properties
|
||||
// from Lua
|
||||
LUA->CreateTable();
|
||||
tableReference = LUA->ReferenceCreate();
|
||||
|
||||
// register some convinence things
|
||||
RegisterGetter("Name", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushString(TImpl::Name()); });
|
||||
}
|
||||
|
||||
LuaObject() = default;
|
||||
~LuaObject() {
|
||||
// free the table reference
|
||||
if(tableReference != -1)
|
||||
lua->ReferenceFree(tableReference);
|
||||
}
|
||||
|
||||
private:
|
||||
// base metamethods
|
||||
LUA_MEMBER_FUNCTION(__gc)
|
||||
LUA_MEMBER_FUNCTION(__index)
|
||||
LUA_MEMBER_FUNCTION(__newindex)
|
||||
|
||||
// static stuff
|
||||
static int __lua_typeid;
|
||||
|
||||
static auto& methods() {
|
||||
static std::unordered_map<std::string, GarrysMod::Lua::CFunc> methods__;
|
||||
return methods__;
|
||||
}
|
||||
|
||||
static auto& getters() {
|
||||
static std::unordered_map<std::string, ILuaFunc> getters__;
|
||||
return getters__;
|
||||
}
|
||||
|
||||
static auto& setters() {
|
||||
static std::unordered_map<std::string, ILuaFunc> setters__;
|
||||
return setters__;
|
||||
}
|
||||
|
||||
// instance stuff
|
||||
int tableReference { -1 };
|
||||
GarrysMod::Lua::ILuaBase* lua;
|
||||
};
|
||||
|
||||
template <class TImpl>
|
||||
int LuaObject<TImpl>::__lua_typeid = 0;
|
||||
|
||||
template <class TImpl>
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __gc) {
|
||||
auto self = FromLua(LUA, 1);
|
||||
if(self != nullptr) {
|
||||
lucore::LogInfo("GCing LuaObject-based object @ {:p}", static_cast<void*>(self));
|
||||
delete self;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class TImpl>
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __index) {
|
||||
auto self = FromLua(LUA, 1);
|
||||
|
||||
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
||||
auto key = GetLuaString(LUA, 2);
|
||||
|
||||
if(methods().find(key) != methods().end()) {
|
||||
LUA->PushCFunction(methods()[key]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(getters().find(key) != getters().end()) {
|
||||
// getters explicitly push their return onto the stack
|
||||
getters()[key](LUA);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// look up from the table
|
||||
// clang-format off
|
||||
LUA->ReferencePush(self->tableReference);
|
||||
LUA->Push(2);
|
||||
LUA->GetTable(-2);
|
||||
// clang-format on
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class TImpl>
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __newindex) {
|
||||
auto self = FromLua(LUA, 1);
|
||||
|
||||
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
||||
auto key = GetLuaString(LUA, 2);
|
||||
|
||||
if(methods().find(key) != methods().end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(getters().find(key) != getters().end() && setters().find(key) == setters().end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(setters().find(key) != setters().end()) {
|
||||
setters()[key](LUA);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// push onto the table
|
||||
// clang-format off
|
||||
LUA->ReferencePush(self->tableReference);
|
||||
LUA->Push(2);
|
||||
LUA->Push(3);
|
||||
LUA->SetTable(-3);
|
||||
LUA->Pop();
|
||||
// clang-format on
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace lcpu::lua
|
||||
|
|
Loading…
Reference in New Issue