diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..450588b9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "googletest"] + path = googletest + url = https://github.com/google/googletest.git +[submodule "Tests/Lua/master"] + path = Tests/Lua/master + url = https://github.com/lua/lua.git + branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..9a1ec251 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required (VERSION 3.8) +include(CMakeDependentOption) + +if (POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() + +project (LuaTemplateLibrary) + +add_subdirectory (Source) +#add_subdirectory (LTL) + +cmake_dependent_option(LTL_TESTING "Build tests" ON + "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF +) + +if(LTL_TESTING) + find_package(GTest) + if(NOT GTest_FOUND) + set(gtest_force_shared_crt ON CACHE BOOL "Use /MD and /MDd" FORCE) + add_subdirectory(googletest EXCLUDE_FROM_ALL) + add_library(GTest::gtest ALIAS gtest) + endif() + + enable_testing() + add_subdirectory(Tests) +endif() + + + diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..1bdb96a0 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,69 @@ +{ + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-debug", + "displayName": "x64 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x64-release", + "displayName": "x64 Release", + "inherits": "x64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "x64-RelWithDebInfo", + "displayName": "x64 Release with DBG info", + "inherits": "x64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "x86-debug", + "displayName": "x86 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x86-release", + "displayName": "x86 Release", + "inherits": "x86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "version": 3 +} diff --git a/LICENSE.txt b/LICENSE.txt index 8aa26455..6552b148 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) [year] [fullname] +Copyright (c) 2023 4z0t Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LuaTemplateLibrary.sln b/LuaTemplateLibrary.sln deleted file mode 100644 index e9558a58..00000000 --- a/LuaTemplateLibrary.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33801.468 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LuaTemplateLibrary", "LuaTemplateLibrary\LuaTemplateLibrary.vcxproj", "{14A3FE99-ADD7-4214-800B-0F9D3AC80A60}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Debug|x64.ActiveCfg = Debug|x64 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Debug|x64.Build.0 = Debug|x64 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Debug|x86.ActiveCfg = Debug|Win32 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Debug|x86.Build.0 = Debug|Win32 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Release|x64.ActiveCfg = Release|x64 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Release|x64.Build.0 = Release|x64 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Release|x86.ActiveCfg = Release|Win32 - {14A3FE99-ADD7-4214-800B-0F9D3AC80A60}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3FE6A2E6-2043-4AB0-BBE9-14D61DE4DA17} - EndGlobalSection -EndGlobal diff --git a/LuaTemplateLibrary/LuaAux.hpp b/LuaTemplateLibrary/LuaAux.hpp deleted file mode 100644 index f744fdcd..00000000 --- a/LuaTemplateLibrary/LuaAux.hpp +++ /dev/null @@ -1,243 +0,0 @@ -#pragma once -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "LuaTypes.hpp" - -namespace Lua -{ - inline void RegisterFunction(lua_State* l, const char* name, lua_CFunction func) - { - lua_pushcfunction(l, func); - lua_setglobal(l, name); - } - - template - inline void _PushValue(lua_State* l, const T* arg); - - template<> - inline void _PushValue(lua_State* l, const char* arg) - { - lua_pushstring(l, arg); - } - - template - inline void _PushValue(lua_State* l, T arg); - - template<> - inline void _PushValue(lua_State* l, lua_Integer arg) - { - lua_pushinteger(l, arg); - } - - template<> - inline void _PushValue(lua_State* l, int arg) - { - TypeParser::Push(l, arg); - } - - template<> - inline void _PushValue(lua_State* l, lua_Number arg) - { - lua_pushnumber(l, arg); - } - - template<> - inline void _PushValue(lua_State* l, float arg) - { - _PushValue(l, static_cast (arg)); - } - - template<> - inline void _PushValue(lua_State* l, std::nullptr_t arg) - { - lua_pushnil(l); - } - - template - size_t _PushArgs(lua_State* l) - { - return N; - } - - template - size_t _PushArgs(lua_State* l, T&& arg, Ts&&... args) - { - _PushValue(l, std::forward>(arg)); - return _PushArgs(l, std::forward(args)...); - } - - template< typename ...Ts> - inline size_t PushArgs(lua_State* l, Ts&& ...args) - { - return _PushArgs<0>(l, std::forward(args)...); - } - - template - inline size_t _PrepareCall(lua_State* l, const char* name, Ts&&... args) - { - lua_getglobal(l, name); - size_t n = PushArgs(l, std::forward(args)...); - return n; - } - - template - void CallFunction(lua_State* l, const char* name, Ts&&... args) - { - size_t n = _PrepareCall(l, name, std::forward(args)...); - lua_call(l, n, 0); - } - - template - bool CallFunctionProtected(lua_State* l, const char* name, Ts&&... args) - { - size_t n = _PrepareCall(l, name, std::forward(args)...); - return lua_pcall(l, n, 0, 0) == LUA_OK; - } - - - template - void RegisterClosure(lua_State* l, const char* name, lua_CFunction func, Ts&&... args) - { - size_t n = PushArgs(l, std::forward(args)...); - lua_pushcclosure(l, func, n); - lua_setglobal(l, name); - } - - template - T GetArg(lua_State* l, int Index); - - template<> - float GetArg(lua_State* l, int Index) - { - return luaL_checknumber(l, Index); - } - - template<> - int GetArg(lua_State* l, int Index) - { - return luaL_checkinteger(l, Index); - } - - template<> - uint8_t GetArg(lua_State* l, int Index) - { - return luaL_checkinteger(l, Index); - } - - template<> - double GetArg(lua_State* l, int Index) - { - return luaL_checknumber(l, Index); - } - - template<> - const char* GetArg(lua_State* l, int Index) - { - return luaL_checkstring(l, Index); - } - - template - constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) - { - return N; - } - - - template - constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) - { - std::get(args) = GetArg(l, N + 1); - return GetArgs(l, args); - } - - template - constexpr size_t GetUpvalues(lua_State* l, TArgsTuple& args) - { - return N; - } - - template - constexpr size_t GetUpvalues(lua_State* l, TArgsTuple& args) - { - std::get(args) = GetArg(l, lua_upvalueindex((int)N + 1)); - return GetUpvalues(l, args); - } - - - template - inline size_t _PushResult(lua_State* l, TResult& result) - { - return Index; - } - - - template - inline size_t _PushResult(lua_State* l, TResult& result) - { - _PushValue(l, std::get(result)); - return _PushResult(l, result); - } - - - template - inline void _PushResult(lua_State* l, T result) - { - _PushValue(l, result); - } - - template - inline void _PushResult(lua_State* l, std::vector& result) - { - lua_createtable(l, result.size(), 0); - for (size_t i = 0; i < result.size(); i++) { - _PushValue(l, result[i]); - lua_rawseti(l, -2, i + 1); - } - } - - template - inline size_t PushResult(lua_State* l, T result) - { - _PushResult(l, result); - return 1; - } - - - template - inline size_t PushResult(lua_State* l, std::tuple& result) - { - return _PushResult<0, std::tuple, Ts...>(l, result); - } - - - template - constexpr size_t _ReplaceUpvalue(lua_State* l, TResult& upvalues) - { - return Index; - } - - template - inline size_t _ReplaceUpvalue(lua_State* l, TResult& upvalues) - { - if constexpr (!std::is_pointer::value) - { - _PushValue(l, std::get(upvalues)); - lua_replace(l, lua_upvalueindex((int)Index + 1)); - } - return _ReplaceUpvalue(l, upvalues); - } - - template - void ReplaceUpvalues(lua_State* l, std::tuple& upvalues) - { - _ReplaceUpvalue<0, std::tuple, CArgs...>(l, upvalues); - } -} \ No newline at end of file diff --git a/LuaTemplateLibrary/LuaClass.hpp b/LuaTemplateLibrary/LuaClass.hpp deleted file mode 100644 index 004bd901..00000000 --- a/LuaTemplateLibrary/LuaClass.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include "LuaAux.hpp" - -namespace Lua -{ - using std::size_t; - - constexpr size_t RegisterSize(const struct luaL_Reg* reg) - { - size_t s = 0; - while ((reg + s)->name != NULL) s++; - return s; - } - - template - class ClassWrapper - { - //static_assert(std::is_same::value); - static_assert(TClass::meta != nullptr, "Metatable required"); - public: - static void Init(lua_State* l, const char* name, const struct luaL_Reg class_reg[]) - { - //create metatable with class name as metatable name - luaL_newmetatable(l, typeid(TClass).name()); - lua_pushstring(l, "__index"); - lua_pushvalue(l, -2); - lua_settable(l, -3); - luaL_setfuncs(l, TClass::meta, 0); - - //create lib - lua_createtable(l, 0, RegisterSize(class_reg)); - luaL_setfuncs(l, class_reg, 0); - lua_setglobal(l, name); - } - }; - -} \ No newline at end of file diff --git a/LuaTemplateLibrary/LuaFunctions.hpp b/LuaTemplateLibrary/LuaFunctions.hpp deleted file mode 100644 index 72796925..00000000 --- a/LuaTemplateLibrary/LuaFunctions.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once -#include "LuaAux.hpp" - -namespace Lua -{ - template - struct CClosure - { - using FnType = decltype(fn); - template - using TReturn = typename std::invoke_result::type; - - template - using ArgsTuple = std::tuple; - using Upvalues = std::tuple; - - - public: - template - static int Function(lua_State* l) - { - using namespace std; - - using Indexes = index_sequence_for; - static_assert(is_invocable::value, "Given function can't be called with such arguments!"); - using ArgsTupleT = ArgsTuple; - - Upvalues upvalues; - GetUpvalues<0, Upvalues, TUpvalues...>(l, upvalues); - ArgsTupleT args; - GetArgs<0, ArgsTupleT, TArgs ...>(l, args); - if constexpr (is_void>::value) - { - CClosure::CallHelper(upvalues, args, index_sequence_for{}, Indexes{}); - ReplaceUpvalues(l, upvalues); - return 0; - } - else - { - TReturn result = CClosure::CallHelper(upvalues, args, index_sequence_for{}, Indexes{}); - ReplaceUpvalues(l, upvalues); - size_t n_results = PushResult(l, result); - return n_results; - } - } - private: - template - inline static TReturn CallHelper(Upvalues& upvalues, ArgsTuple& args, std::index_sequence const, std::index_sequence const) - { - return fn(std::get(upvalues)..., std::get(args)...); - } - }; - - template - struct CFunction : public CClosure { - }; - - - template - struct ClassClosure - { - template - using TReturn = typename std::invoke_result::type; - - template - using ArgsTuple = std::tuple; - using Upvalues = std::tuple; - - - public: - template - static int Function(lua_State* l) - { - using namespace std; - - using Indexes = index_sequence_for; - static_assert(std::is_invocable::value, "Given class doesnt provide callable or can't be called with such arguments!"); - using ArgsTupleT = ArgsTuple; - - Upvalues upvalues; - GetUpvalues<0, Upvalues, TUpvalues...>(l, upvalues); - ArgsTupleT args; - GetArgs<0, ArgsTupleT, TArgs ...>(l, args); - if constexpr (is_void>::value) - { - ClassClosure::CallHelper(l, upvalues, args, index_sequence_for{}, Indexes{}); - ReplaceUpvalues(l, upvalues); - return 0; - } - else - { - TReturn result = ClassClosure::CallHelper(l, upvalues, args, index_sequence_for{}, Indexes{}); - ReplaceUpvalues(l, upvalues); - size_t n_results = PushResult(l, result); - return n_results; - } - } - private: - template - inline static TReturn CallHelper(lua_State* l, Upvalues& upvalues, ArgsTuple& args, std::index_sequence const, std::index_sequence const) - { - - if constexpr (std::is_constructible::value) - { - return FnClass{ l }(std::get(upvalues)..., std::get(args)...); - } - else - { - return FnClass{}(std::get(upvalues)..., std::get(args)...); - } - } - }; - - template - struct ClassFunction : public ClassClosure { - }; -} \ No newline at end of file diff --git a/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj b/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj deleted file mode 100644 index 4d229cfc..00000000 --- a/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {14a3fe99-add7-4214-800b-0f9d3ac80a60} - LuaTemplateLibrary - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj.filters b/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj.filters deleted file mode 100644 index f911260d..00000000 --- a/LuaTemplateLibrary/LuaTemplateLibrary.vcxproj.filters +++ /dev/null @@ -1,47 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Исходные файлы - - - Исходные файлы - - - - - Файлы заголовков - - - Файлы заголовков - - - Файлы заголовков - - - Файлы заголовков - - - Файлы заголовков - - - - - Файлы ресурсов - - - \ No newline at end of file diff --git a/LuaTemplateLibrary/LuaTypes.hpp b/LuaTemplateLibrary/LuaTypes.hpp deleted file mode 100644 index eb89d336..00000000 --- a/LuaTemplateLibrary/LuaTypes.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -namespace Lua -{ - template - struct TypeParser - { - static T Get(lua_State* l, int index); - - static bool Check(lua_State* l, int index); - - static void Push(lua_State* l, T && value); - }; - - template<> - struct TypeParser - { - static int Get(lua_State* l, int index) - { - return luaL_checkinteger(l, index); - } - - static bool Check(lua_State* l, int index) - { - return lua_isinteger(l, index); - } - - static void Push(lua_State* l, int value) - { - lua_pushinteger(l, value); - } - }; - -} diff --git a/LuaTemplateLibrary/Tests.cpp b/LuaTemplateLibrary/Tests.cpp deleted file mode 100644 index 6431f2a3..00000000 --- a/LuaTemplateLibrary/Tests.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - - -#include "Tests.h" -#include - -#include "LuaFunctions.hpp" -#include "LuaClass.hpp" - - - -namespace Tests -{ - struct TestClass - { - static const char* className; - - - static int Print(lua_State* l) - { - std::cout << "Hello!" << std::endl; - - return 0; - } - - - static int Create(lua_State* l) - { - size_t nbytes = sizeof(TestClass); - TestClass* a = (TestClass*)lua_newuserdata(l, nbytes); - - luaL_getmetatable(l, TestClass::className); - lua_setmetatable(l, -2); - - return 1; /* new userdatum is already on the stack */ - } - - - inline static const luaL_Reg meta[] = { - {"print", TestClass::Print}, - {"aboba", TestClass::Print}, - {NULL, NULL} - }; - - inline static const luaL_Reg _class[] = { - {"new", TestClass::Create}, - {NULL, NULL} - }; - }; - - const char* TestClass::className = typeid(TestClass).name(); - -} - - -class Callable -{ -public: - - Callable(lua_State* l) - { - std::cout << "called with lua state in it" << std::endl; - } - - int operator()(int a, int b) - { - return a * a + b; - } - - int operator()(int a, int b, int c) - { - return a * a + b * c; - } -}; - -template -struct MakeArray -{ - std::vector operator()(int n) - { - return std::vector(n); - } -}; - -void PrintClosureNumber2(int& a, float& b) -{ - a++; - b += 0.1; - std::cout << "Value is " << a << " " << b << std::endl; -} - - -void Say(const char* str) -{ - std::cout << "Value is " << str << std::endl; -} - - -float myfunc(float a, float b) -{ - return a * b - a / b; -} - - -inline double Gamma(double a) -{ - return tgamma(a); -} - -inline float Hypot(float a, float b) -{ - return hypotf(a, b); -} - -void Test() -{ - using namespace std; - lua_State* l = luaL_newstate(); - luaL_openlibs(l); - using namespace Tests; - Lua::ClassWrapper::Init(l, "TestClass", TestClass::_class); - - std::cout << TestClass::className << std::endl; - - Lua::RegisterFunction(l, "MakeArray", Lua::ClassFunction>::Function); - Lua::RegisterFunction(l, "DoubleInt", Lua::ClassFunction::Function); - Lua::RegisterFunction(l, "TripleInt", Lua::ClassFunction::Function); - Lua::RegisterClosure(l, "PrintInc", Lua::CClosure::Function<>, 7, 3.2f); - Lua::RegisterClosure(l, "SayHello", Lua::CClosure::Function<>, "Hello!"); - Lua::RegisterClosure(l, "SayBye", Lua::CClosure::Function<>, "Bye!"); - - Lua::RegisterClosure(l, "SayFoo", Lua::CClosure::Function, "Foo"); - Lua::RegisterFunction(l, "Say", Lua::CFunction::Function); - Lua::RegisterFunction(l, "Gamma", Lua::CFunction::Function); - Lua::RegisterFunction(l, "Hypot", Lua::CFunction::Function); - Lua::RegisterFunction(l, "MyFunc", Lua::CFunction::Function); - - - - if (luaL_dofile(l, "main.lua")) - { - cout << "error:" << lua_tostring(l, -1) << std::endl; - } - - Lua::CallFunction(l, "Main"); - -} \ No newline at end of file diff --git a/LuaTemplateLibrary/Tests.h b/LuaTemplateLibrary/Tests.h deleted file mode 100644 index b430fc56..00000000 --- a/LuaTemplateLibrary/Tests.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void Test(); \ No newline at end of file diff --git a/LuaTemplateLibrary/main.cpp b/LuaTemplateLibrary/main.cpp deleted file mode 100644 index fdf3e8fe..00000000 --- a/LuaTemplateLibrary/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ - - -#include "Tests.h" - -int main() -{ - Test(); -} \ No newline at end of file diff --git a/LuaTemplateLibrary/main.lua b/LuaTemplateLibrary/main.lua deleted file mode 100644 index 96b3abcc..00000000 --- a/LuaTemplateLibrary/main.lua +++ /dev/null @@ -1,27 +0,0 @@ -function Test() - - local t = MakeArray(10) - print(#t) - for i = 1, #t do - print(t[i]) - end - print(DoubleInt(2, 3)) - - local tc = TestClass.new() - tc:print() - tc:aboba() - PrintInc() - PrintInc() - PrintInc() - - SayHello() - SayBye() - SayFoo() - Say(1) - print(Gamma(0.5)) - print(MyFunc(2, 5)) -end - -function Main( ) - Test() -end diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt new file mode 100644 index 00000000..5f6ec2a1 --- /dev/null +++ b/Source/CMakeLists.txt @@ -0,0 +1,36 @@ +set(LTL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/LuaTemplateLibrary) + +set(LTL_HEADERS + ${LTL_DIR}/Internal.hpp + ${LTL_DIR}/FuncArguments.hpp + ${LTL_DIR}/FuncUtils.hpp + ${LTL_DIR}/LuaAux.hpp + ${LTL_DIR}/UserData.hpp + ${LTL_DIR}/Property.hpp + ${LTL_DIR}/ClassConstructor.hpp + ${LTL_DIR}/Class.hpp + ${LTL_DIR}/LuaFunctions.hpp + ${LTL_DIR}/LuaState.hpp + ${LTL_DIR}/LuaTypes.hpp + ${LTL_DIR}/RefObject.hpp + ${LTL_DIR}/Exception.hpp + ${LTL_DIR}/LTL.hpp +) + +source_group("LTL" FILES ${LTL_HEADERS}) + +add_library(LuaTemplateLibrary INTERFACE) + +set_target_properties(LuaTemplateLibrary PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + ) + + +target_sources(LuaTemplateLibrary INTERFACE + ${LTL_HEADERS} +) + +target_include_directories(LuaTemplateLibrary INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/Class.hpp b/Source/LuaTemplateLibrary/Class.hpp new file mode 100644 index 00000000..379ed283 --- /dev/null +++ b/Source/LuaTemplateLibrary/Class.hpp @@ -0,0 +1,227 @@ +#pragma once +#include "LuaAux.hpp" +#include "UserData.hpp" +#include "LuaFunctions.hpp" +#include "LuaState.hpp" +#include "ClassConstructor.hpp" +#include "Property.hpp" + +#define lua_regptr_isnt_set(l, p) assert(lua_getregp(l, p) == LUA_TNIL) + +namespace Lua +{ + template + struct Class; + + struct MethodBase {}; + + template + struct UserDataValueClassWrapper + { + template + struct AddUserDataValue : TypeBase {}; + + template<> + struct AddUserDataValue : TypeBase> {}; + + template + using AUDV_t = typename AddUserDataValue::type; + }; + + template + class Method : private UserDataValueClassWrapper, public MethodBase + { + public: + using TClass = Class; + using UserDataValueClassWrapper::AUDV_t; + + Method() = default; + + static void AddMethod(TClass& c, const char* name) + { + auto method = Closure, AUDV_t...>::Function; + c.AddMetaMethod(name, method); + } + }; + + + template + class Method : private UserDataValueClassWrapper, public MethodBase + { + public: + using TClass = Class; + using UserDataValueClassWrapper::AUDV_t; + + Method() = default; + + static void AddMethod(TClass& c, const char* name) + { + auto method = Closure(UserDataValue, AUDV_t...)>::Function; + c.AddMetaMethod(name, method); + } + }; + + template + struct Class + { + using UData = UserData; + + Class(lua_State* l, const char* name) :m_state(l), m_name(name) + { + MakeMetaTable(); + MakeClassTable(); + if constexpr (!std::is_trivially_destructible_v) + { + //std::cout << "Assigning dtor for " << typeid(T).name() << std::endl; + AddMetaMethod("__gc", UData::DestructorFunction); + } + } + + Class(const State& state, const char* name) : Class(state.GetState()->Unwrap(), name) {} + + template + Class& AddConstructor() + { + RegisterFunction(m_state, m_name, Constructor::Function); + return *this; + } + + template + Class& AddMethod(const char* name, const TMethod&) + { + TMethod::AddMethod(*this, name); + + return *this; + } + + template + Class& AddProperty(const char* name, const TProperty&) + { + return AddGetter(name, TProperty::Get) + .AddSetter(name, TProperty::Set); + } + + Class& AddGetter(const char* name, lua_CFunction func) + { + MakeIndexTable(); + UData::IndexTable::Push(m_state); + RawSetFunction(name, func); + return *this; + } + + Class& AddSetter(const char* name, lua_CFunction func) + { + MakeNewIndexTable(); + UData::NewIndexTable::Push(m_state); + RawSetFunction(name, func); + return *this; + } + + Class& AddMetaMethod(const char* name, lua_CFunction func) + { + UData::MetaTable::Push(m_state); + RawSetFunction(name, func); + return *this; + } + + Class& SetIndexFunction(lua_CFunction func) + { + return this->AddMetaMethod("__index", func); + } + + Class& SetNewIndexFunction(lua_CFunction func) + { + return this->AddMetaMethod("__newindex", func); + } + + template + using EnableIfBaseOf = std::enable_if_t, Class&>; + + template + EnableIfBaseOf Add(const char* name, const Element& element) + { + return this->AddMethod(name, element); + } + + template + EnableIfBaseOf Add(const char* name, const Element& element) + { + return this->AddProperty(name, element); + } + + template + EnableIfBaseOf Add(const char* name, const Element& element) + { + return AddGetter(name, Element::Function); + } + + template + EnableIfBaseOf Add(const char* name, const Element& element) + { + return AddSetter(name, Element::Function); + } + + private: + + void RawSetFunction(const char* name, lua_CFunction func) + { + lua_pushstring(m_state, name); + lua_pushcfunction(m_state, func); + lua_rawset(m_state, -3); + lua_pop(m_state, 1); + } + + void MakeMetaTable() + { + lua_regptr_isnt_set(m_state, UData::MetaTable::GetKey()); + + lua_newtable(m_state); + lua_pushstring(m_state, "__index"); + lua_pushvalue(m_state, -2); + lua_rawset(m_state, -3); + lua_setregp(m_state, UData::MetaTable::GetKey()); + } + + void MakeClassTable() + { + lua_regptr_isnt_set(m_state, UData::ClassTable::GetKey()); + + lua_newtable(m_state); + lua_pushstring(m_state, "className"); + lua_pushstring(m_state, m_name.c_str()); + lua_rawset(m_state, -3); + lua_setregp(m_state, UData::ClassTable::GetKey()); + } + + void MakeIndexTable() + { + if (m_has_index_table) + return; + lua_regptr_isnt_set(m_state, UData::IndexTable::GetKey()); + + SetIndexFunction(UData::IndexMethod); + + lua_newtable(m_state); + lua_setregp(m_state, UData::IndexTable::GetKey()); + m_has_index_table = true; + } + + void MakeNewIndexTable() + { + if (m_has_newindex_table) + return; + lua_regptr_isnt_set(m_state, UData::NewIndexTable::GetKey()); + + SetNewIndexFunction(UData::NewIndexMethod); + + lua_newtable(m_state); + lua_setregp(m_state, UData::NewIndexTable::GetKey()); + m_has_newindex_table = true; + } + + lua_State* m_state; + std::string m_name; + bool m_has_index_table = false; + bool m_has_newindex_table = false; + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/ClassConstructor.hpp b/Source/LuaTemplateLibrary/ClassConstructor.hpp new file mode 100644 index 00000000..799eee6b --- /dev/null +++ b/Source/LuaTemplateLibrary/ClassConstructor.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaTypes.hpp" +#include "FuncArguments.hpp" +#include "UserData.hpp" + + +namespace Lua +{ + template + struct Constructor + { + using ArgsTuple = std::tuple...>; + + static int Function(lua_State* l) + { + ArgsTuple args; + Constructor::GetArgs(l, args); + void* obj = lua_newuserdata(l, sizeof(C)); + UnpackArgs(obj, args, std::index_sequence_for{}); + UserData::SetClassMetaTable(l); + return 1; + } + + private: + static constexpr size_t GetArgs(lua_State* l, ArgsTuple& args) + { + return FuncUtility::GetArgs<0, 0, ArgsTuple, TArgs...>(l, args); + } + + template + static constexpr void UnpackArgs(void* const object, ArgsTuple& args, const std::index_sequence) + { + return Make(object, std::get(args)...); + } + + static constexpr void Make(void* const object, Unwrap_t& ...args) + { + static_assert(std::is_constructible_v...>, "Object of class C can't be constructed with such arguments!"); + new(object) C(args...); + } + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/Exception.hpp b/Source/LuaTemplateLibrary/Exception.hpp new file mode 100644 index 00000000..1e3f8716 --- /dev/null +++ b/Source/LuaTemplateLibrary/Exception.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "LuaAux.hpp" + +namespace Lua +{ + class Exception : public std::runtime_error + { + public: + Exception(lua_State* l, int index) :std::runtime_error(GetReason(l, index)), m_state(l) {} + + lua_State* GetState()const + { + return m_state; + } + + static int PanicFunc(lua_State* l) + { + throw Exception(l, -1); + } + private: + static const char* GetReason(lua_State* l, int index) + { + const char* reason = lua_tostring(l, index); + return reason ? reason : "Unknown reason"; + } + lua_State* m_state; + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/FuncArguments.hpp b/Source/LuaTemplateLibrary/FuncArguments.hpp new file mode 100644 index 00000000..16d77e7d --- /dev/null +++ b/Source/LuaTemplateLibrary/FuncArguments.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +namespace Lua +{ + + struct UnwrapTypeBase {}; + + template + struct TypeBase :UnwrapTypeBase + { + using type = T; + }; + + struct OptionalArg {}; + + template + struct OptionalBase :OptionalArg, TypeBase {}; + + template + struct Unwrap + { + using type = T; + }; + + template + struct Unwrap>> + { + using type = typename T::type; + }; + + template + using Unwrap_t = typename Unwrap::type; + + template + struct Default : TypeBase + { + static T value; + }; + + template + T Default::value{}; + + template + struct Upvalue :TypeBase {}; +} + +#define LuaOptionalArg(name_, type_, value_) \ +struct name_ : Lua::OptionalBase\ +{\ + static const type_ value; \ +};\ +const type_ name_::value = (value_) \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/FuncUtils.hpp b/Source/LuaTemplateLibrary/FuncUtils.hpp new file mode 100644 index 00000000..da4e4fed --- /dev/null +++ b/Source/LuaTemplateLibrary/FuncUtils.hpp @@ -0,0 +1,147 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaTypes.hpp" +#include "FuncArguments.hpp" +#include "LuaState.hpp" + +namespace Lua +{ + namespace FuncUtility + { + template + struct IsUpvalueType : std::false_type { using type = void; }; + + template + struct IsUpvalueType> : std::true_type { using type = typename T; }; + + template<> + struct IsUpvalueType : std::true_type { using type = StateWrap*; }; + + template<> + struct IsUpvalueType : std::true_type { using type = lua_State*; }; + + template + using ValueContainer = std::integral_constant; + + template + struct Incrementer; + + template + struct Incrementer : ValueContainer {}; + + template + struct Incrementer : ValueContainer {}; + + template + struct IncrementArgIndex : Incrementer::value, Index> {}; + + template + struct IncrementUpvalueIndex : Incrementer::value, Index> {}; + + template + struct ArgExtractor + { + template + static constexpr typename StackType::TReturn Get(lua_State* l) + { + return StackType::Get(l, ArgI + 1); + } + }; + + template + struct ArgExtractor> + { + template + static constexpr typename StackType::TReturn Get(lua_State* l) + { + if (StackType::Check(l, ArgI + 1)) + return StackType::Get(l, ArgI + 1); + return Default::value; + } + }; + + template + struct ArgExtractor> + { + template + static constexpr typename StackType::TReturn Get(lua_State* l) + { + return StackType::Get(l, lua_upvalueindex(static_cast(UpvalueI) + 1)); + } + }; + + + template + struct ArgExtractor>> + { + using ReturnT = typename T::type; + template + static constexpr ReturnT Get(lua_State* l) + { + if (StackType::Check(l, ArgI + 1)) + return StackType::Get(l, ArgI + 1); + return T::value; + } + }; + + template + struct UpvalueReplacer + { + template + static constexpr void Replace(lua_State* l, const T& value) + { + if constexpr (!std::is_pointer_v) + { + PushValue(l, value); + lua_replace(l, lua_upvalueindex(static_cast(UpvalueI) + 1)); + } + } + }; + + template + constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) + { + return ArgIndex + UpvalueIndex; + } + + template + constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) + { + std::get(args) = ArgExtractor::Get(l); + return GetArgs< + IncrementArgIndex::value, + IncrementUpvalueIndex::value, + TArgsTuple, TArgs...>(l, args); + } + + template + constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) + { + return GetArgs<0, 0, TArgsTuple, TArgs...>(l, args); + } + + template + constexpr size_t ReplaceUpvalues(lua_State* l, TArgsTuple& args) + { + return ArgIndex + UpvalueIndex; + } + + template + constexpr size_t ReplaceUpvalues(lua_State* l, TArgsTuple& args) + { + if constexpr (IsUpvalueType::value) + UpvalueReplacer::type>::Replace(l, std::get(args)); + return ReplaceUpvalues< + IncrementArgIndex::value, + IncrementUpvalueIndex::value, + TArgsTuple, Ts...>(l, args); + } + + template + constexpr size_t ReplaceUpvalues(lua_State* l, TArgsTuple& args) + { + return ReplaceUpvalues<0, 0, TArgsTuple, Ts...>(l, args); + } + + } +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/Internal.hpp b/Source/LuaTemplateLibrary/Internal.hpp new file mode 100644 index 00000000..d118cf8a --- /dev/null +++ b/Source/LuaTemplateLibrary/Internal.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Lua/LuaLibrary.h" + +#define STATIC_FAIL(message) static_assert(false, message) + +inline void lua_setregp(lua_State* l, const void* p) +{ + return lua_rawsetp(l, LUA_REGISTRYINDEX, p); +} + +inline int lua_getregp(lua_State* l, const void* p) +{ + return lua_rawgetp(l, LUA_REGISTRYINDEX, p); +} + +namespace Lua::Internal +{ + template + struct IntParser + { + static_assert(std::is_integral_v, "Provided not integral type"); + + using TReturn = T; + + static TReturn Get(lua_State* l, int index) + { + return static_cast(luaL_checkinteger(l, index)); + } + + static bool Check(lua_State* l, int index) + { + return lua_isinteger(l, index); + } + + static void Push(lua_State* l, const T& value) + { + lua_pushinteger(l, static_cast(value)); + } + }; + + template + struct FloatParser + { + static_assert(std::is_floating_point_v, "Provided not floating point type"); + + using TReturn = T; + + static TReturn Get(lua_State* l, int index) + { + return static_cast(luaL_checknumber(l, index)); + } + + static bool Check(lua_State* l, int index) + { + return lua_isnumber(l, index); + } + + static void Push(lua_State* l, const T& value) + { + lua_pushnumber(l, static_cast(value)); + } + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/LTL.hpp b/Source/LuaTemplateLibrary/LTL.hpp new file mode 100644 index 00000000..ee9840d8 --- /dev/null +++ b/Source/LuaTemplateLibrary/LTL.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "Internal.hpp" +#include "LuaTypes.hpp" +#include "LuaAux.hpp" +#include "Property.hpp" +#include "ClassConstructor.hpp" +#include "Class.hpp" +#include "UserData.hpp" +#include "Exception.hpp" +#include "FuncArguments.hpp" +#include "FuncUtils.hpp" +#include "LuaFunctions.hpp" +#include "LuaState.hpp" +#include "RefObject.hpp" diff --git a/Source/LuaTemplateLibrary/LuaAux.hpp b/Source/LuaTemplateLibrary/LuaAux.hpp new file mode 100644 index 00000000..4b43615e --- /dev/null +++ b/Source/LuaTemplateLibrary/LuaAux.hpp @@ -0,0 +1,194 @@ +#pragma once +#include "LuaTypes.hpp" + +namespace Lua +{ + template + using const_decay_t = std::decay_t; + + struct StackPopper + { + StackPopper(lua_State* l, int n) : m_state(l), n(n) {} + + ~StackPopper() + { + lua_pop(m_state, n); + } + private: + lua_State* m_state; + int n; + }; + + void RegisterFunction(lua_State* l, const char* name, lua_CFunction func) + { + lua_pushcfunction(l, func); + lua_setglobal(l, name); + } + + void RegisterFunction(lua_State* l, const std::string& name, lua_CFunction func) + { + return RegisterFunction(l, name.c_str(), func); + } + + template + T GetValue(lua_State* l, int index) + { + return StackType::Get(l, index); + } + + template + inline void PushValue(lua_State* l, const T& arg) + { + StackType>::Push(l, arg); + } + + template + size_t _PushArgs(lua_State* l) + { + return N; + } + + template + size_t _PushArgs(lua_State* l, T&& arg, Ts&&... args) + { + PushValue(l, std::forward(arg)); + return _PushArgs(l, std::forward(args)...); + } + + template< typename ...Ts> + inline size_t PushArgs(lua_State* l, Ts&& ...args) + { + return _PushArgs<0>(l, std::forward(args)...); + } + + template + inline size_t _PrepareCall(lua_State* l, const char* name, Ts&&... args) + { + lua_getglobal(l, name); + return PushArgs(l, std::forward(args)...); + } + + template + TReturn CallStack(lua_State* l, const size_t n_args) + { + if constexpr (std::is_void_v) + { + lua_call(l, static_cast(n_args), 0); + } + else + { + StackPopper pop{ l, 1 }; + lua_call(l, static_cast(n_args), 1); + return GetValue(l, -1); + } + } + + template + TReturn CallFunction(lua_State* l, const char* name, Ts&&... args) + { + size_t n = _PrepareCall(l, name, std::forward(args)...); + return CallStack(l, n); + } + + template + bool CallFunctionProtected(lua_State* l, const char* name, Ts&&... args) + { + size_t n = _PrepareCall(l, name, std::forward(args)...); + return lua_pcall(l, n, 0, 0) == LUA_OK; + } + + + template + void RegisterClosure(lua_State* l, const char* name, lua_CFunction func, Ts&&... args) + { + size_t n = PushArgs(l, std::forward(args)...); + lua_pushcclosure(l, func, static_cast(n)); + lua_setglobal(l, name); + } + + template + constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) + { + return N; + } + + template + constexpr size_t GetArgs(lua_State* l, TArgsTuple& args) + { + std::get(args) = GetValue(l, N + 1); + return GetArgs(l, args); + } + + template + constexpr size_t GetUpvalues(lua_State* l, TArgsTuple& args) + { + return N; + } + + template + constexpr size_t GetUpvalues(lua_State* l, TArgsTuple& args) + { + std::get(args) = GetValue(l, lua_upvalueindex((int)N + 1)); + return GetUpvalues(l, args); + } + + + template + inline size_t _PushResult(lua_State* l, TResult& result) + { + return Index; + } + + + template + inline size_t _PushResult(lua_State* l, TResult& result) + { + _PushValue(l, std::get(result)); + return _PushResult(l, result); + } + + + template + inline void _PushResult(lua_State* l, const T& result) + { + PushValue(l, result); + } + + template + inline size_t PushResult(lua_State* l, T result) + { + _PushResult(l, result); + return 1; + } + + + template + inline size_t PushResult(lua_State* l, std::tuple& result) + { + return _PushResult<0, std::tuple, Ts...>(l, result); + } + + + template + constexpr size_t _ReplaceUpvalue(lua_State* l, TResult& upvalues) + { + return Index; + } + + template + inline size_t _ReplaceUpvalue(lua_State* l, TResult& upvalues) + { + if constexpr (!std::is_pointer::value) + { + PushValue(l, std::get(upvalues)); + lua_replace(l, lua_upvalueindex((int)Index + 1)); + } + return _ReplaceUpvalue(l, upvalues); + } + + template + void ReplaceUpvalues(lua_State* l, std::tuple& upvalues) + { + _ReplaceUpvalue<0, std::tuple, CArgs...>(l, upvalues); + } +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/LuaFunctions.hpp b/Source/LuaTemplateLibrary/LuaFunctions.hpp new file mode 100644 index 00000000..4a1617eb --- /dev/null +++ b/Source/LuaTemplateLibrary/LuaFunctions.hpp @@ -0,0 +1,313 @@ +#pragma once +#include "LuaAux.hpp" +#include "FuncArguments.hpp" +#include "FuncUtils.hpp" + +namespace Lua +{ + template + struct CClosure + { + using FnType = decltype(fn); + template + using TReturn = typename std::invoke_result::type; + + template + using ArgsTuple = std::tuple; + using Upvalues = std::tuple; + + + public: + template + static int Function(lua_State* l) + { + using namespace std; + + using Indexes = index_sequence_for; + static_assert(is_invocable::value, "Given function can't be called with such arguments!"); + using ArgsTupleT = ArgsTuple; + + Upvalues upvalues; + GetUpvalues<0, Upvalues, TUpvalues...>(l, upvalues); + ArgsTupleT args; + GetArgs<0, ArgsTupleT, TArgs ...>(l, args); + if constexpr (is_void>::value) + { + CClosure::CallHelper(upvalues, args, index_sequence_for{}, Indexes{}); + ReplaceUpvalues(l, upvalues); + return 0; + } + else + { + TReturn result = CClosure::CallHelper(upvalues, args, index_sequence_for{}, Indexes{}); + ReplaceUpvalues(l, upvalues); + size_t n_results = PushResult(l, result); + return static_cast(n_results); + } + } + private: + template + inline static TReturn CallHelper(Upvalues& upvalues, ArgsTuple& args, std::index_sequence const, std::index_sequence const) + { + return fn(std::get(upvalues)..., std::get(args)...); + } + }; + + template + struct CFunction : public CClosure { + }; + + + template + struct ClassClosure + { + template + using TReturn = typename std::invoke_result::type; + + template + using ArgsTuple = std::tuple; + using Upvalues = std::tuple; + + + public: + template + static int Function(lua_State* l) + { + using namespace std; + + using Indexes = index_sequence_for; + static_assert(std::is_invocable::value, "Given class doesnt provide callable or can't be called with such arguments!"); + using ArgsTupleT = ArgsTuple; + + Upvalues upvalues; + GetUpvalues<0, Upvalues, TUpvalues...>(l, upvalues); + ArgsTupleT args; + GetArgs<0, ArgsTupleT, TArgs ...>(l, args); + if constexpr (is_void>::value) + { + ClassClosure::CallHelper(l, upvalues, args, index_sequence_for{}, Indexes{}); + ReplaceUpvalues(l, upvalues); + return 0; + } + else + { + TReturn result = ClassClosure::CallHelper(l, upvalues, args, index_sequence_for{}, Indexes{}); + ReplaceUpvalues(l, upvalues); + size_t n_results = PushResult(l, result); + return static_cast(n_results); + } + } + private: + template + inline static TReturn CallHelper(lua_State* l, Upvalues& upvalues, ArgsTuple& args, std::index_sequence const, std::index_sequence const) + { + + if constexpr (std::is_constructible::value) + { + return FnClass{ l }(std::get(upvalues)..., std::get(args)...); + } + else + { + return FnClass{}(std::get(upvalues)..., std::get(args)...); + } + } + }; + + template + struct ClassFunction : public ClassClosure { + }; + + template + struct Closure + { + using FnType = decltype(fn); + + static_assert(std::is_invocable_v&...>, "Given function can't be called with such arguments!"); + using TReturn = std::invoke_result_t&...>; + + using ArgsTuple = std::tuple...>; + using Indexes = std::index_sequence_for; + + public: + static int Function(lua_State* l) + { + ArgsTuple args; + Closure::GetArgs(l, args); + if constexpr (std::is_void_v) + { + Closure::Call(args, Indexes{}); + Closure::ReplaceUpvalues(l, args); + return 0; + } + else + { + TReturn result = Closure::Call(args, Indexes{}); + Closure::ReplaceUpvalues(l, args); + size_t n_results = PushResult(l, result); + return static_cast(n_results); + } + } + private: + static constexpr size_t GetArgs(lua_State* l, ArgsTuple& args) + { + return FuncUtility::GetArgs<0, 0, ArgsTuple, TArgs...>(l, args); + } + + static constexpr size_t ReplaceUpvalues(lua_State* l, ArgsTuple& args) + { + return FuncUtility::ReplaceUpvalues<0, 0, ArgsTuple, TArgs...>(l, args); + } + + template + struct CallHelper; + + template + struct CallHelper + { + template + static TReturn Call(Args&... args) + { + return fn(args...); + } + }; + + template + struct CallHelper + { + template + static TReturn Call(Class& arg, Args&... args) + { + if constexpr (std::is_pointer_v) + { + return (arg->*fn)(args...); + } + else + { + return (arg.*fn)(args...); + } + } + }; + + template + struct CallHelper + { + template + static TReturn Call(const Class& arg, Args&... args) + { + if constexpr (std::is_pointer_v) + { + return (arg->*fn)(args...); + } + else + { + return (arg.*fn)(args...); + } + } + }; + + template + inline static TReturn Call(ArgsTuple& args, const std::index_sequence) + { + return CallHelper::Call(std::get(args)...); + } + }; + + + template + struct Closure + { + using FnType = decltype(fn); + + static_assert(std::is_invocable_v&...>, "Given function can't be called with such arguments!"); + using UnwrapperReturn = std::invoke_result_t&...>; + + static_assert(std::is_same_v == std::is_same_v, "If function returns void you cannot return anything else but void!"); + + using ArgsTuple = std::tuple...>; + using Indexes = std::index_sequence_for; + + public: + static int Function(lua_State* l) + { + ArgsTuple args; + Closure::GetArgs(l, args); + if constexpr (std::is_void_v) + { + Closure::Call(args, Indexes{}); + Closure::ReplaceUpvalues(l, args); + return 0; + } + else + { + UnwrapperReturn result = Closure::Call(args, Indexes{}); + Closure::ReplaceUpvalues(l, args); + StackType::Push(l, std::move(result)); + return 1; + } + } + private: + static constexpr size_t GetArgs(lua_State* l, ArgsTuple& args) + { + return FuncUtility::GetArgs<0, 0, ArgsTuple, TArgs...>(l, args); + } + + static constexpr size_t ReplaceUpvalues(lua_State* l, ArgsTuple& args) + { + return FuncUtility::ReplaceUpvalues<0, 0, ArgsTuple, TArgs...>(l, args); + } + + template + struct CallHelper; + + template + struct CallHelper + { + template + static UnwrapperReturn Call(Args&... args) + { + return fn(args...); + } + }; + + template + struct CallHelper + { + template + static UnwrapperReturn Call(Class& arg, Args&... args) + { + if constexpr (std::is_pointer_v) + { + return (arg->*fn)(args...); + } + else + { + return (arg.*fn)(args...); + } + } + }; + + template + struct CallHelper + { + template + static UnwrapperReturn Call(const Class& arg, Args&... args) + { + if constexpr (std::is_pointer_v) + { + return (arg->*fn)(args...); + } + else + { + return (arg.*fn)(args...); + } + } + }; + + template + inline static UnwrapperReturn Call(ArgsTuple& args, const std::index_sequence) + { + return CallHelper::Call(std::get(args)...); + } + }; + +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/LuaState.hpp b/Source/LuaTemplateLibrary/LuaState.hpp new file mode 100644 index 00000000..c8d193ba --- /dev/null +++ b/Source/LuaTemplateLibrary/LuaState.hpp @@ -0,0 +1,231 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaTypes.hpp" +#include "Exception.hpp" + +namespace Lua +{ + StateWrap* WrapState(lua_State* l) + { + return (StateWrap*)l; + } + + class StateWrap final + { + public: + + inline static StateWrap* Wrap(lua_State* l) + { + return WrapState(l); + } + + inline static StateWrap* Create() + { + return Wrap(luaL_newstate()); + } + + inline static lua_State* Unwrap(const StateWrap* s) + { + return (lua_State*)s; + } + + inline lua_State* Unwrap()const + { + return StateWrap::Unwrap(this); + } + + inline lua_State* Unwrap() + { + return StateWrap::Unwrap(this); + } + + void OpenLibs() + { + return luaL_openlibs(Unwrap()); + } + + bool DoFile(const char* name) + { + return luaL_dofile(Unwrap(), name); + } + + template + void Push(const T& value) + { + return PushValue(Unwrap(), value); + } + + void Pop(size_t n = 1) + { + return lua_pop(Unwrap(), static_cast(n)); + } + + void Close() + { + lua_close(Unwrap()); + } + + template + void RegisterClosure(const char* name, lua_CFunction func, Ts&&... args) + { + Lua::RegisterClosure(Unwrap(), name, func, std::forward(args)...); + } + + template + TReturn Call(const char* name, Ts&&... args) + { + return Lua::CallFunction(Unwrap(), name, std::forward(args)...); + } + + template + T Get(int index) + { + return GetValue(Unwrap(), index); + } + + void Run(const char* const s) throw(Exception) + { + if (luaL_dostring(Unwrap(), s)) + { + lua_error(Unwrap()); + } + } + + lua_CFunction SetAtPanicFuntion(lua_CFunction func) + { + return lua_atpanic(Unwrap(), func); + } + + void Remove(int index) + { + return lua_remove(Unwrap(), index); + } + + void Rotate(int index, int n) + { + return lua_rotate(Unwrap(), index, n); + } + + void Duplicate(int index) + { + return lua_pushvalue(Unwrap(), index); + } + + void SetTable(int index) + { + return lua_settable(Unwrap(), index); + } + + int GetTable(int index) + { + return lua_gettable(Unwrap(), index); + } + + template + const char* PushFormatString(const char* fmt, const Ts& ...args) + { + return lua_pushfstring(Unwrap(), fmt, args...); + } + + private: + StateWrap() = delete; + ~StateWrap() = delete; + + }; + + class State + { + public: + State() + { + m_state = StateWrap::Create(); + } + State(const State&) = delete; + State& operator=(const State&) = delete; + + ~State() + { + if (m_state) + m_state->Close(); + m_state = nullptr; + } + + void ThrowExceptions() + { + m_state->SetAtPanicFuntion(Exception::PanicFunc); + } + + void OpenLibs() + { + return m_state->OpenLibs(); + } + + template + void Push(const T& value) + { + return m_state->Push(value); + } + + void Pop(size_t n) + { + return m_state->Pop(n); + } + + State& AddFunction(const char* const name, lua_CFunction func) + { + return this->AddClosure(name, func); + } + + template + State& AddClosure(const char* const name, lua_CFunction func, Ts&&... args) + { + m_state->RegisterClosure(name, func, std::forward(args)...); + return *this; + } + + bool DoFile(const char* const path) + { + return m_state->DoFile(path); + } + + template + TReturn Call(const char* name, Ts&&... args) + { + return m_state->Call(name, std::forward(args)...); + } + + template + T To(int index) + { + return m_state->Get(index); + } + + const StateWrap* GetState()const + { + return m_state; + } + + void Run(const char* const s) throw(Exception) + { + return m_state->Run(s); + } + + void Run(const std::string& s) + { + return Run(s.c_str()); + } + + template + State& SetGlobal(const char* name, const T& value) + { + PushValue(m_state->Unwrap(), value); + lua_setglobal(m_state->Unwrap(), name); + + return *this; + } + + private: + StateWrap* m_state = nullptr; + + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/LuaTypes.hpp b/Source/LuaTemplateLibrary/LuaTypes.hpp new file mode 100644 index 00000000..cb265a1b --- /dev/null +++ b/Source/LuaTemplateLibrary/LuaTypes.hpp @@ -0,0 +1,228 @@ +#pragma once +#include "Internal.hpp" + +namespace Lua +{ + class StateWrap; + + StateWrap* WrapState(lua_State* l); + + enum class Type + { + None = LUA_TNONE, + Nil = LUA_TNIL, + Boolean = LUA_TBOOLEAN, + Lightdata = LUA_TLIGHTUSERDATA, + Number = LUA_TNUMBER, + String = LUA_TSTRING, + Table = LUA_TTABLE, + Function = LUA_TFUNCTION, + Userdata = LUA_TUSERDATA, + Thread = LUA_TTHREAD, + }; + + template + struct StackType + { + using TReturn = T; + + static T Get(lua_State* l, int index) + { + STATIC_FAIL("Not provided implementation for Get function"); + return {}; + } + + static bool Check(lua_State* l, int index) + { + STATIC_FAIL("Not provided implementation for Check function"); + } + + static void Push(lua_State* l, const T& value) + { + STATIC_FAIL("Not provided implementation for Push function"); + } + }; + + template<> + struct StackType + { + using TReturn = lua_State*; + + static TReturn Get(lua_State* l, int index) + { + return l; + } + }; + + template<> + struct StackType + { + using TReturn = StateWrap*; + + static TReturn Get(lua_State* l, int index) + { + return WrapState(l); + } + }; + + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + template<> + struct StackType : Internal::IntParser {}; + + + /* template<> + struct StackType + { + static bool Check(lua_State* l, int index) + { + return lua_isnil(l, index); + } + + static void Push(lua_State* l, std::nullptr_t) + { + lua_pushnil(l); + } + };*/ + + template<> + struct StackType + { + static bool Check(lua_State* l, int index) + { + return lua_isnil(l, index); + } + }; + + template<> + struct StackType : Internal::FloatParser {}; + template<> + struct StackType : Internal::FloatParser {}; + template<> + struct StackType : Internal::FloatParser {}; + + template<> + struct StackType + { + using TReturn = bool; + + static TReturn Get(lua_State* l, int index) + { + return lua_toboolean(l, index); + } + + static bool Check(lua_State* l, int index) + { + return lua_isboolean(l, index); + } + + static void Push(lua_State* l, bool value) + { + lua_pushboolean(l, value); + } + }; + + template<> + struct StackType + { + using TReturn = const char*; + + static TReturn Get(lua_State* l, int index) + { + return luaL_checkstring(l, index); + } + + static bool Check(lua_State* l, int index) + { + return lua_isstring(l, index); + } + + static void Push(lua_State* l, const char* value) + { + lua_pushstring(l, value); + } + }; + + template<> + struct StackType : public StackType + { + using TReturn = const char*; + + static TReturn Get(lua_State* l, int index) + { + return { StackType::Get(l, index) }; + } + + static void Push(lua_State* l, const std::string& value) + { + StackType::Push(l, value.c_str()); + } + }; + + template<> + struct StackType + { + using TReturn = lua_CFunction; + + static TReturn Get(lua_State* l, int index) + { + return lua_tocfunction(l, index); + } + + static bool Check(lua_State* l, int index) + { + return lua_iscfunction(l, index); + } + + static void Push(lua_State* l, lua_CFunction value) + { + lua_pushcfunction(l, value); + } + }; + + template + struct StackType> + { + using TReturn = std::vector; + + static TReturn Get(lua_State* l, int index) + { + lua_pushvalue(l, index); + auto size = lua_rawlen(l, -1); + std::vector result(size); + for (size_t i = 0; i < size; i++) { + lua_rawgeti(l, -1, i + 1); + result[i] = StackType::Get(l, -1); + lua_pop(l, 1); + } + lua_pop(l, 1); + return result; + } + + static bool Check(lua_State* l, int index) + { + return lua_istable(l, index); + } + + static void Push(lua_State* l, const std::vector& value) + { + lua_createtable(l, value.size(), 0); + for (size_t i = 0; i < value.size(); i++) { + PushValue(l, value[i]); + lua_rawseti(l, -2, i + 1); + } + } + }; +} diff --git a/Source/LuaTemplateLibrary/Property.hpp b/Source/LuaTemplateLibrary/Property.hpp new file mode 100644 index 00000000..5ab98b01 --- /dev/null +++ b/Source/LuaTemplateLibrary/Property.hpp @@ -0,0 +1,93 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaTypes.hpp" +#include "FuncArguments.hpp" +#include "UserData.hpp" + + +namespace Lua +{ + + struct GetterBase {}; + struct SetterBase {}; + struct PropertyBase {}; + + template + struct Getter :public GetterBase + { + static int Function(lua_State* l) + { + C* ud = UserData::ValidateUserData(l, 1); + PushValue(l, ud->*Field); + return 1; + } + + }; + + template + struct Setter :public SetterBase + { + static int Function(lua_State* l) + { + C* ud = UserData::ValidateUserData(l, 1); + T value = StackType::Get(l, 2); + ud->*Field = std::move(value); + return 0; + } + }; + + template + struct Property : public PropertyBase + { + static int Get(lua_State* l) + { + return Getter::Function(l); + } + + static int Set(lua_State* l) + { + return Setter::Function(l); + } + }; + + /*template + struct AProperty + { + using TField = decltype(Field); + + static int Get(lua_State* l) + { + return __Get::Call(l); + } + + static int Set(lua_State* l) + { + return __Set::Call(l); + } + private: + template + struct __Get; + + template + struct __Set; + + template + struct __Get + { + static int Call(lua_State* l) + { + return Getter::Function(l); + } + }; + + template + struct __Set + { + static int Call(lua_State* l) + { + return Getter::Function(l); + } + }; + };*/ + +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/RefObject.hpp b/Source/LuaTemplateLibrary/RefObject.hpp new file mode 100644 index 00000000..20d96693 --- /dev/null +++ b/Source/LuaTemplateLibrary/RefObject.hpp @@ -0,0 +1,604 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaState.hpp" +#include "LuaTypes.hpp" + +namespace Lua +{ + + struct RefGlobalAccess + { + static int GetRef(lua_State* l) + { + return luaL_ref(l, LUA_REGISTRYINDEX); + } + + static void Unref(lua_State* l, int ref) + { + luaL_unref(l, LUA_REGISTRYINDEX, ref); + } + + static void PushRef(lua_State* l, int ref) + { + lua_rawgeti(l, LUA_REGISTRYINDEX, ref); + } + }; + + template + class RefObjectBase + { + public: + RefObjectBase() {}; + RefObjectBase(lua_State* l) noexcept : m_state(l) { assert(m_state, "Expected not null lua_State"); }; + RefObjectBase(const State& state) noexcept : RefObjectBase(state.GetState()->Unwrap()) {}; + + class AutoPop + { + public: + AutoPop(const RefObjectBase& obj) : m_obj(obj) + { + m_obj.Push(); + } + ~AutoPop() + { + m_obj.Pop(); + } + const RefObjectBase& m_obj; + }; + friend class AutoPop; + + class Iterator + { + public: + Iterator(const ParentClass& iterable) : m_table(iterable), m_key(iterable.GetState()) {} + + Iterator& Next() + { + lua_State* l = m_table.GetState(); + m_table.Push(); + m_key.Push(); + if (lua_next(l, -2) != 0) + { + + m_value = ParentClass::FromTop(l); + m_key = ParentClass::FromTop(l); + } + else + { + m_key.Unref(); + m_value.Unref(); + } + lua_pop(l, 1); + return *this; + } + + bool operator==(const Iterator& other)const + { + return m_table == other.m_table && m_key == other.m_key; + } + + bool operator!=(const Iterator& other)const + { + return !(*this == other); + } + + Iterator& operator++() + { + return this->Next(); + } + + const std::pair operator*()const + { + return { m_key,m_value }; + } + + ParentClass operator->()const + { + return m_value; + } + + ParentClass Key()const { return m_key; } + ParentClass Value()const { return m_value; } + + private: + ParentClass m_table{}; + ParentClass m_key{}; + ParentClass m_value{}; + }; + friend class Iterator; + + Iterator begin()const { return Iterator(ParentClass(_This())).Next(); }; + Iterator end()const { return Iterator(ParentClass(_This())); } + + template + T To()const + { + AutoPop pop(*this); + return StackType::Get(m_state, -1); + } + + template + operator T()const + { + return this->To(); + } + + template + bool operator<(const T& other)const + { + return this->Compare(other); + } + + template + bool operator<=(const T& other)const + { + return this->Compare(other); + } + + template + bool operator==(const T& other)const + { + return this->Compare(other); + } + + template + bool operator>(const T& other)const + { + return !(*this <= other); + } + + template + bool operator>=(const T& other)const + { + return !(*this < other); + } + + template + bool operator!=(const T& other)const + { + return !(*this == other); + } + + template + TReturn Call(TArgs&& ...args) + { + Push(); + size_t n = PushArgs(m_state, std::forward(args)...); + return CallStack(m_state, n); + } + + template + TReturn SelfCall(const char* key, TArgs&& ...args) + { + Push(); + lua_getfield(m_state, -1, key); + lua_rotate(m_state, -2, 1); + size_t n = PushArgs(m_state, std::forward(args)...) + 1; + return CallStack(m_state, n); + } + + template + ParentClass operator()(TArgs&& ...args) + { + return this->Call(std::forward(args)...); + } + + template + bool Is()const + { + AutoPop pop(*this); + return StackType::Check(m_state, -1); + } + + bool IsNil()const + { + AutoPop pop(*this); + return lua_isnil(m_state, -1); + } + + bool IsTable()const + { + AutoPop pop(*this); + return lua_istable(m_state, -1); + } + + Type Type()const + { + AutoPop pop(*this); + return static_cast(lua_type(m_state, -1)); + } + + const char* TypeName()const + { + AutoPop pop(*this); + return lua_typename(m_state, lua_type(m_state, -1)); + } + + const char* ToString()const + { + lua_getglobal(m_state, "tostring"); + AutoPop pop(*this); + lua_call(m_state, 1, 1); + return lua_tostring(m_state, -1); + } + + friend static std::ostream& operator<<(std::ostream& os, const RefObjectBase& obj) + { + return os << obj.ToString(); + } + + lua_State* GetState()const + { + return this->m_state; + } + + ~RefObjectBase() + { + Unref(); + Clear(); + } + + private: + template + bool Compare(const T& value)const + { + StackPopper pop{ m_state, 2 }; + this->Push(); + PushValue(m_state, value); + return lua_compare(m_state, -2, -1, COMPARE_OP); + } + + void Unref() + { + _This().Unref(); + } + + void Clear() + { + _This().Clear(); + } + + void Push()const + { + _This().Push(); + } + + void Pop()const + { + _This().Pop(); + } + protected: + void PushRef(int ref)const + { + RefAccess::PushRef(m_state, ref); + } + + int GetRef() + { + return RefAccess::GetRef(m_state); + } + + void UnrefRef(int ref) + { + RefAccess::Unref(m_state, ref); + } + + private: + const RefClass& _This() const { return static_cast(*this); } + RefClass& _This() { return static_cast(*this); } + protected: + lua_State* m_state = nullptr; + }; + + template + class RefObject; + + template + class RefTableObject; + + template + class RefObject : public RefObjectBase, RefObject, RefAccess> + { + public: + using Base = RefObjectBase, RefObject, RefAccess>; + using RefTableObjectT = RefTableObject; + friend class Base; + using Base::Base; + + RefObject(const RefObject& obj) noexcept : Base(obj.m_state) + { + obj.Push(); + Ref(); + } + + template + RefObject(lua_State* l, const T& value) noexcept :Base(l) + { + PushValue(l, value); + Ref(); + }; + + RefObject(RefObject&& obj) noexcept : Base(obj.m_state) + { + m_ref = obj.m_ref; + obj.Clear(); + } + + RefObject(const RefTableObjectT& obj) noexcept : Base(obj.m_state) + { + obj.Push(); + Ref(); + } + + template + RefObject& operator=(const T& value) + { + Unref(); + PushValue(this->m_state, value); + Ref(); + return *this; + } + + RefObject& operator=(const RefObject& obj) + { + Unref(); + obj.Push(); + this->m_state = obj.m_state; + Ref(); + return *this; + } + + RefObject& operator=(const RefTableObjectT& obj) + { + Unref(); + obj.Push(); + this->m_state = obj.m_state; + Ref(); + return *this; + } + + RefObject& operator=(RefObject&& obj) noexcept + { + Unref(); + m_ref = obj.m_ref; + this->m_state = obj.m_state; + obj.Clear(); + return *this; + } + + template + RefTableObjectT operator[](const T& key) + { + RefTableObjectT obj{ this->m_state }; + PushValue(this->m_state, key); + obj.m_key_ref = this->GetRef(); + Push(); + obj.m_table_ref = this->GetRef(); + return obj; + } + + static RefObject Global(lua_State* l, const char* key) + { + RefObject obj{ l }; + lua_getglobal(l, key); + obj.Ref(); + return obj; + } + + static RefObject Global(const State& state, const char* key = "_G") + { + return Global(state.GetState()->Unwrap(), key); + } + + static RefObject MakeTable(lua_State* l, int narr = 0, int nhash = 0) + { + RefObject obj{ l }; + lua_createtable(l, narr, nhash); + obj.Ref(); + return obj; + } + + static RefObject MakeTable(const State& state, int narr = 0, int nhash = 0) + { + return MakeTable(state.GetState()->Unwrap(), narr, nhash); + } + + static RefObject FromStack(lua_State* l, int index) + { + RefObject obj{ l }; + lua_pushvalue(l, index); + obj.Ref(); + return obj; + } + + static RefObject FromStack(const State& state, int index) + { + return FromStack(state.GetState()->Unwrap(), index); + } + + static RefObject FromTop(lua_State* l) + { + RefObject obj{ l }; + obj.Ref(); + return obj; + } + + static RefObject FromTop(const State& state) + { + return FromTop(state.GetState()->Unwrap()); + } + + void Push()const + { + this->PushRef(m_ref); + } + + void Unref() + { + if (this->m_state) + { + this->UnrefRef(m_ref); + m_ref = LUA_NOREF; + } + } + private: + void Ref() + { + m_ref = this->GetRef(); + } + + + void Clear() + { + this->m_state = nullptr; + m_ref = LUA_NOREF; + } + + void Pop()const + { + lua_pop(this->m_state, 1); + } + + int m_ref = LUA_NOREF; + }; + + template + class RefTableObject : public RefObjectBase, RefObject, RefAccess> + { + public: + using RefClass = RefObject; + using Base = RefObjectBase, RefAccess>; + friend class RefClass; + friend class Base; + + template + RefTableObject operator[](const T& key) + { + return RefClass(*this)[key]; + } + + template + RefTableObject& operator=(const T& value) + { + PushTable(); + PushKey(); + PushValue(this->m_state, value); + lua_settable(this->m_state, -3); + Pop(); + return *this; + } + + template<> + RefTableObject& operator=(const RefClass& obj) + { + PushTable(); + PushKey(); + obj.Push(); + lua_settable(this->m_state, -3); + Pop(); + return *this; + } + + RefTableObject& operator=(const RefTableObject& obj) + { + return *this = RefClass(obj); + } + + void Push()const + { + PushTable(); + PushKey(); + lua_gettable(this->m_state, -2); + lua_remove(this->m_state, -2); + } + + void Unref() + { + if (this->m_state) + { + this->UnrefRef(m_table_ref); + this->UnrefRef(m_key_ref); + m_table_ref = LUA_NOREF; + m_key_ref = LUA_NOREF; + } + } + private: + RefTableObject() :Base() {}; + RefTableObject(lua_State* l) noexcept :Base(l) { }; + RefTableObject(const State& state) noexcept : Base(state) {}; + RefTableObject(const RefTableObject& obj) : Base(obj.m_state) + { + obj.PushKey(); + m_key_ref = this->GetRef(); + + obj.PushTable(); + m_table_ref = this->GetRef(); + } + + void PushKey()const + { + this->PushRef(m_key_ref); + } + + void PushTable()const + { + this->PushRef(m_table_ref); + } + + void Clear() + { + this->m_state = nullptr; + m_table_ref = LUA_NOREF; + m_key_ref = LUA_NOREF; + } + + void Pop()const + { + lua_pop(this->m_state, 1); + } + + int m_table_ref = LUA_NOREF; + int m_key_ref = LUA_NOREF; + }; + + using GRefObject = RefObject; + + template + struct StackType> + { + using TReturn = RefObject; + + static TReturn Get(lua_State* l, int index) + { + return RefObject::FromStack(l, index); + } + + static bool Check(lua_State* l, int index) + { + return !lua_isnone(l, index); + } + + static void Push(lua_State* l, const RefObject& value) + { + assert(l == value.GetState()); + value.Push(); + } + }; + + template + struct StackType> + { + using Type = RefTableObject; + + static bool Check(lua_State* l, int index) + { + return !lua_isnone(l, index); + } + + static void Push(lua_State* l, const Type& value) + { + assert(l == value.GetState()); + value.Push(); + } + }; +} \ No newline at end of file diff --git a/Source/LuaTemplateLibrary/UserData.hpp b/Source/LuaTemplateLibrary/UserData.hpp new file mode 100644 index 00000000..b2bc8f77 --- /dev/null +++ b/Source/LuaTemplateLibrary/UserData.hpp @@ -0,0 +1,189 @@ +#pragma once +#include "LuaAux.hpp" +#include "LuaTypes.hpp" +#include "FuncArguments.hpp" +#include "Exception.hpp" + +namespace Lua +{ + template + struct RegistryTableBase + { + static const void* GetKey() + { + static const char key; + return &key; + } + + static int Push(lua_State* l) + { + return lua_getregp(l, RegistryTableBase::GetKey()); + } + }; + + template + struct UserData + { + static void CopyFunction(lua_State* l, T&& other) + { + static_assert(std::is_copy_constructible_v, "Can't copy construct type T!"); + new(lua_newuserdata(l, sizeof(T))) T(std::forward(other)); + SetClassMetaTable(l); + } + + static void SetClassMetaTable(lua_State* l) + { + if (MetaTable::Push(l) != LUA_TTABLE) + { + throw std::logic_error("The class was't registered"); + } + lua_setmetatable(l, -2); + } + + static int DestructorFunction(lua_State* l) + { + T* data = static_cast(lua_touserdata(l, 1)); + data->~T(); + + return 0; + } + + static void ThrowInvalidUserData(lua_State* l, int index) + { + luaL_argerror(l, index, "invalind UserData"); + } + + static void ThrowWrongUserDataType(lua_State* l, int index) + { + luaL_error(l, "Expected %s but got userdata", GetClassName(l)); + } + + static void ThrowWrongType(lua_State* l, int index) + { + luaL_error(l, "Expected %s but got %s", GetClassName(l), lua_typename(l, lua_type(l, index))); + } + + static T* ValidateUserData(lua_State* l, int index) + { + if (!lua_isuserdata(l, index)) + { + ThrowWrongType(l, index); + return nullptr; + } + + if (!lua_getmetatable(l, index)) + { + ThrowInvalidUserData(l, index); + return nullptr; + } + + if (MetaTable::Push(l) == LUA_TNIL) + { + lua_pop(l, 1); + return false; + } + + bool res = lua_rawequal(l, -2, -1); + lua_pop(l, 2); + + if (!res) + { + ThrowWrongUserDataType(l, index); + return nullptr; + } + + return static_cast(lua_touserdata(l, index)); + } + + struct MetaTable : public RegistryTableBase {}; + struct ClassTable : public RegistryTableBase {}; + struct IndexTable : public RegistryTableBase {}; + struct NewIndexTable : public RegistryTableBase {}; + + static int IndexMethod(lua_State* l) + { + IndexTable::Push(l); + lua_pushvalue(l, 2); + int type = lua_rawget(l, -2); + if (type == LUA_TNIL) + { + lua_pop(l, 2); // pop nil and index table + + MetaTable::Push(l); + lua_pushvalue(l, 2); + lua_rawget(l, -2); // get value in metatable + + lua_remove(l, -2); // remove metatable + + return 1; + } + + lua_remove(l, -2); // remove index table + lua_pushvalue(l, 1); // push userdata + + assert(lua_isuserdata(l, -1)); + assert(lua_isfunction(l, -2)); + + lua_call(l, 1, 1); + + return 1; + } + + static int NewIndexMethod(lua_State* l) + { + NewIndexTable::Push(l); + lua_pushvalue(l, 2); + int type = lua_rawget(l, -2); + if (type == LUA_TNIL) + { + lua_pop(l, 2); // pop nil and index table + + const char* s = lua_tostring(l, 2); + luaL_error(l, "Attempt to set field '%s' on %s", s ? s : "NONSTRINGVALUE", GetClassName(l)); + + return 1; + } + lua_remove(l, -2); // remove newindex table + lua_pushvalue(l, 1); // push userdata + lua_pushvalue(l, 3); // push value + + assert(lua_isuserdata(l, -2)); + assert(lua_isfunction(l, -3)); + + lua_call(l, 2, 0); + + return 0; + } + + static const char* GetClassName(lua_State* l) + { + StackPopper pop{ l, 2 }; + ClassTable::Push(l); + lua_pushstring(l, "className"); + lua_rawget(l, -2); + return lua_tostring(l, -1); + } + + }; + + template + struct UserDataValue : TypeBase {}; + + template + struct StackType> + { + using TReturn = T*; + + static TReturn Get(lua_State* l, int index) + { + return UserData::ValidateUserData(l, index); + } + + static void Push(lua_State* l, T&& value) + { + UserData::CopyFunction(l, std::forward(value)); + } + + }; + +} \ No newline at end of file diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 00000000..64a7d89b --- /dev/null +++ b/Tests/CMakeLists.txt @@ -0,0 +1,58 @@ +set(LTL_TEST_SOURCE_FILES + Source/RefObject.cpp + Source/Exception.cpp + Source/Main.cpp +) + +source_group("Source" FILES ${LTL_TEST_SOURCE_FILES}) + +file(GLOB LUA54_FILES + Lua/master/*.c + Lua/master/*.h + Lua/master/*.hpp + Lua/LuaLibrary.h +) +list(FILTER LUA54_FILES EXCLUDE REGEX ".*lua\\.c$") + +add_library(Lua54 ${LUA54_FILES}) +target_compile_definitions(Lua54 PUBLIC LUA_VERSION=504) +target_include_directories(Lua54 PUBLIC Lua/master) +source_group("Source" FILES ${LUA54_FILES}) + +add_executable(TestLTL + ${LTL_TEST_SOURCE_FILES} +) + +add_executable(FreeTest_LTL + Main.cpp +) + +set_target_properties(TestLTL FreeTest_LTL PROPERTIES + CXX_STANDARD 17 +) + +target_include_directories(TestLTL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(TestLTL + LuaTemplateLibrary + Lua54 + GTest::gtest +) +target_link_libraries(FreeTest_LTL + LuaTemplateLibrary + Lua54 +) + +target_compile_options(FreeTest_LTL PRIVATE "$<$:/Zi;/EHa>") +target_link_options(FreeTest_LTL PRIVATE "$<$:/DEBUG>") +target_link_options(FreeTest_LTL PRIVATE "$<$:/OPT:REF>") +target_link_options(FreeTest_LTL PRIVATE "$<$:/OPT:ICF>") + +add_custom_command( + TARGET FreeTest_LTL POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/main.lua + ${CMAKE_CURRENT_BINARY_DIR}/main.lua) + + +add_test(TestLTL TestLTL) \ No newline at end of file diff --git a/Tests/Lua/LuaLibrary.h b/Tests/Lua/LuaLibrary.h new file mode 100644 index 00000000..144f3d19 --- /dev/null +++ b/Tests/Lua/LuaLibrary.h @@ -0,0 +1,14 @@ +#pragma once + +#if LUA_VERSION >= 501 + +extern "C" { +#include "lauxlib.h" +#include "lua.h" +#include "lualib.h" +} +#else + +#error "LUA_VERSION is not defined" + +#endif diff --git a/Tests/Lua/master b/Tests/Lua/master new file mode 160000 index 00000000..6baee9ef --- /dev/null +++ b/Tests/Lua/master @@ -0,0 +1 @@ +Subproject commit 6baee9ef9d5657ab582c8a4b9f885ec58ed502d0 diff --git a/Tests/Main.cpp b/Tests/Main.cpp new file mode 100644 index 00000000..d2779271 --- /dev/null +++ b/Tests/Main.cpp @@ -0,0 +1,466 @@ +#pragma once +#define LUA_VERSION 504 +#include +#include +#include +#include "Lua/LuaLibrary.h" +#include "LuaTemplateLibrary/LTL.hpp" + +class Callable +{ +public: + + Callable(lua_State* l) + { + std::cout << "called with lua state in it" << std::endl; + } + + int operator()(int a, int b) + { + std::cout << "2 args\n"; + return a * a + b; + } + + int operator()(int a, int b, int c) + { + std::cout << "3 args\n"; + return a * a + b * c; + } +}; + +template +struct MakeArray +{ + std::vector operator()(int n) + { + return std::vector(n); + } + + +}; + +template +std::vector DoubleArray(const std::vector& arr) +{ + std::vector result(arr.size() * 2); + for (size_t i = 0; i < result.size(); i++) + { + result[i] = arr[i % arr.size()]; + } + return result; +} + + +void PrintClosureNumber2(int& a, float& b) +{ + a++; + b += 0.1; + std::cout << "Value is " << a << " " << b << std::endl; +} + + +void Say(const std::string& str) +{ + std::cout << "Value is " << str << std::endl; +} + + +float myfunc(float a, float b) +{ + return a * b - a / b; +} + + +float TestDefault(float a, float b) +{ + return a * b; +} + +inline double Gamma(double a) +{ + return tgamma(a); +} + +inline float Hypot(float a, float b) +{ + return hypotf(a, b); +} + + +struct Vector3f +{ + float x, y, z; + + Vector3f(float x, float y, float z) :x(x), y(y), z(z) + { + + } + Vector3f() :Vector3f(0, 0, 0) {} + + static float Length(const Vector3f& v) + { + return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); + } + + static Vector3f Sum(const Vector3f& v1, const Vector3f& v2) + { + return { v1.x + v2.x , v1.y + v2.y , v1.z + v2.z }; + } + + std::string ToString(Lua::StateWrap* state)const + { + std::string s{ state->PushFormatString("Vector { %f, %f, %f }", x, y, z) }; + state->Pop(); + return s; + } + + /* Vector3f operator+(const Vector3f& v)const + { + return Sum(*this, v); + }*/ + + Vector3f operator+(const Vector3f* v)const + { + return Sum(*this, *v); + } +}; + +template<> +struct Lua::StackType +{ + using TReturn = Vector3f; + + static bool Check(lua_State* l, int index) + { + return lua_istable(l, index); + } + + static Vector3f Get(lua_State* l, int index) + { + if (!lua_istable(l, index)) + { + return { 0,0,0 }; + } + Vector3f res; + lua_pushvalue(l, index); + lua_rawgeti(l, -1, 1); + res.x = lua_tonumber(l, -1); + lua_rawgeti(l, -2, 2); + res.y = lua_tonumber(l, -1); + lua_rawgeti(l, -3, 3); + res.z = lua_tonumber(l, -1); + lua_pop(l, 4); + return res; + } + + static void Push(lua_State* l, const Vector3f& vec) + { + lua_createtable(l, 3, 0); + lua_pushnumber(l, vec.x); + lua_rawseti(l, -2, 1); + lua_pushnumber(l, vec.y); + lua_rawseti(l, -2, 2); + lua_pushnumber(l, vec.z); + lua_rawseti(l, -2, 3); + } +}; + +template<> +struct Lua::StackType +{ + using TReturn = Vector3f*; + + static bool Check(lua_State* l, int index) + { + return lua_isuserdata(l, index); + } + + static Vector3f* Get(lua_State* l, int index) + { + void* ud = lua_touserdata(l, index); + + return (Vector3f*)ud; + } + + static void Push(lua_State* l, const Vector3f* vec) + { + new (lua_newuserdata(l, sizeof(Vector3f))) Vector3f(*vec); + } +}; + +double GetSystemTime() { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count() / 1000.0; +} + +LuaOptionalArg(OptStringValue, std::string, "My string"); +LuaOptionalArg(OptionalDoubleHalf, double, 0.5); +LuaOptionalArg(OptionalDouble1, double, 1); + +void CoolFunction(Lua::GRefObject& obj) +{ + using namespace std; + cout << obj["Lua"].ToString() << endl; + obj["Lua"] = "Yes"; + +} + +void Test() +{ + using namespace std; + using namespace Lua; + Lua::State lua_state{}; + lua_state.OpenLibs(); + { + + Vector3f v{ 1,2,3 }; + lua_state.AddFunction("MakeArray", Lua::ClassFunction>::Function) + .AddFunction("DoubleArray", Lua::CFunction>::Function>) + .AddFunction("DoubleInt", Lua::ClassFunction::Function) + .AddFunction("TripleInt", Lua::ClassFunction::Function) + .AddClosure("SayHello", Lua::CClosure::Function<>, "Hello!") + .AddFunction("VectorLen", Lua::CFunction ::Function) + .AddFunction("VectorSum", Lua::CFunction ::Function) + .AddClosure("SayFoo", Lua::CClosure::Function, "Foo") + .AddFunction("Say", Lua::CFunction::Function) + .AddFunction("Gamma", Lua::CFunction::Function) + .AddFunction("Hypot", Lua::CFunction::Function) + .AddFunction("MyFunc", Lua::Closure::Function) + .AddFunction("Def", Lua::Closure>::Function) + .AddClosure("Upval", Lua::Closure>::Function, 1.f) + .AddClosure("Opt", Lua::Closure::Function) + .AddClosure("PrintInc", Lua::Closure, Upvalue>::Function, 7, 3.2f) + .AddClosure("SayBye", Lua::Closure::Function) + .AddFunction("GetSystemTime", Lua::CFunction::Function<>) + //.AddFunction("VecSum2", Lua::Closure<&Vector3f::operator+, Vector3f, Vector3f>::Function) + //.AddClosure("VecPtr", Lua::Closure<&Vector3f::operator+, Upvalue, Vector3f>::Function, &v) + //.AddClosure("CoolFunction", Lua::Closure::Function) + ; + } + + + if (lua_state.DoFile("main.lua")) + { + Lua::GRefObject obj = Lua::GRefObject::FromStack(lua_state, -1); + + cout << obj.Is() << std::endl; + cout << "error:" << lua_state.To(-1) << std::endl; + return; + } + + Lua::GRefObject obj2(lua_state); + cout << obj2.IsNil() << endl; + + GRefObject obj3 = GRefObject::MakeTable(lua_state); + cout << obj3.IsTable() << endl; + cout << obj3.TypeName() << endl; + + GRefObject obj4{ lua_state }; + obj4 = "Hello world"; + cout << obj4.ToString() << endl; + obj4 = 4; + cout << obj4.ToString() << endl; + obj4 = 4.5f; + cout << obj4.ToString() << endl; + obj4 = 4.3; + cout << obj4.ToString() << endl; + obj4 = obj3; + cout << obj4.ToString() << endl; + cout << obj4.TypeName() << endl; + + obj4["Hi"] = "Bruh"; + cout << obj4["Hi"].ToString() << endl; + GRefObject obj5{ lua_state }; + obj5 = "Bro"; + obj4["Hi"] = obj5; + cout << obj4["Hi"].ToString() << endl; + obj4["Hi"] = obj4; + obj4["Hi"]["Hi"] = "No"; + + obj4["hi"] = obj4["Hi"]; + cout << obj4["hi"].TypeName() << endl; + cout << obj4["hi"].ToString() << endl; + + obj4[1.1] = 2.5; + obj4[obj4[1.1]] = 1.1; + obj4[obj4] = "TABLE"; + cout << obj4[1.1].ToString() << endl; + cout << obj4[2.5].ToString() << endl; + cout << obj4[obj4[1.1]].ToString() << endl; + cout << obj4[obj4[2.5]].ToString() << endl; + cout << obj4[obj4].ToString() << endl; + obj4["func"] = Closure::Function; + + cout << lua_state.Call("Main", obj4) << endl; + + lua_state.Run("s = { a = 4 }"); + GRefObject s = GRefObject::Global(lua_state, "s"); + GRefObject global = GRefObject::Global(lua_state); + cout << s.TypeName() << endl; + cout << s.ToString() << endl; + cout << s["a"].TypeName() << endl; + cout << s["a"] << endl; + cout << s["a"].To() << endl; + cout << (int)s["a"] << endl; + + cout << (s == s) << endl; + cout << (global["s"] == s) << endl; + cout << (s["a"] == s) << endl; + cout << (s["a"] == 4) << endl; + cout << (s == 4) << endl; + lua_state.Run("t = { a = 4, b = 5, c = {1,2,3,4,5} , 1,2,3}"); + GRefObject v = GRefObject::Global(lua_state, "t"); + for (auto& [key, value] : v) + { + cout << key << ":" << value << endl; + } + for (auto& [key, value] : v["c"]) + { + cout << key << ":" << value << endl; + } + cout << (s["a"] == s["a"]) << endl; + + global["bool"] = true; + cout << global["bool"] << endl; + cout << global["RefFunc"](1) << endl; + global["RefFunc"].Call(1); + + + lua_state.Push(3); + lua_state.Push(2); + lua_state.Push(1); + cout << lua_gettop(lua_state.GetState()->Unwrap()) << endl; + +} + +struct MyUData +{ + MyUData() :MyUData(1, 2, 3) + { + + } + + MyUData(int a, int b, int c) :a(a), b(b), c(c) + { + + } + + //MyUData(const MyUData&) = delete; + + int GetA()const + { + return a; + } + + void SetA(int a) + { + this->a = a; + } + + void Hello(const char* s)const + { + std::cout << "Hello" << a << b << c << s << std::endl; + } + + void Hello2(const char* s, const MyUData* other)const + { + std::cout << "Hello" << a << b << c << s << other->a << std::endl; + } + + ~MyUData() + { + //std::cout << "GG" << std::endl; + } + + // todo such methods + MyUData Double()const + { + return MyUData{ a * 2,b * 2,c * 2 }; + } + + + int a, b, c; +}; +template +using UDValue = Lua::UserDataValue; + +void Print(const MyUData* data) +{ + using namespace std; + cout << "MyUData: " << data->a << ' ' << data->b << ' ' << data->c << endl; +} + +void ClassTest() +{ + using namespace Lua; + using namespace std; + try + { + + State lua_state{}; + lua_state.OpenLibs(); + lua_state.ThrowExceptions(); + Class(lua_state, "MyClass") + .AddConstructor, Default, Default>() + .Add("GetA", Method{}) + .Add("SetA", Method{}) + .Add("Hello", Method{}) + .Add("Hello2", Method{}) + .Add("Hello3", Method{}) + .Add("Double", Method{}) + .Add("a", Getter{}) + .Add("b", Setter{}) + .Add("Print", Method{}) + ; + + Class(lua_state, "Vector") + .AddConstructor, Default, Default>() + .Add("x", Property{}) + .Add("y", Property{}) + .Add("z", Property{}) + .Add("__add", Method{}) + .Add("__tostring", Method{}) + ; + + cout << typeid(&Vector3f::ToString).name() << endl; + cout << typeid(string(Vector3f::*)(StateWrap*)const).name() << endl; + + + //cout << typeid(UserDataValueClassWrapper::AddUserDataValue::type).name() << endl; + lua_state.Run( + "local v = Vector(1,2,3) " + "print(v.x) " + "print(v.y) " + "print(v.z) " + "print(v.w) " + "v.x = 5 " + //"v.w = 1 " + "print(v.x) " + "local ud = MyClass(2,3,4) " + "print(ud.a) " + "print(ud.a) " + "ud.b = 5 " + "print(ud.b) " + "ud:Double():Print() " + "local v1 = Vector(1,2,3) " + "local v2 = Vector(4,5,6) " + "print(v1+v2) " + "v1.w = 6 " + "print(v1+v2) " + ); + + + + + } + catch (Exception& ex) + { + std::cerr << ex.what() << std::endl; + } +} + +int main() +{ + ClassTest(); + //Test(); +} \ No newline at end of file diff --git a/Tests/Source/Exception.cpp b/Tests/Source/Exception.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Tests/Source/Main.cpp b/Tests/Source/Main.cpp new file mode 100644 index 00000000..26109e0f --- /dev/null +++ b/Tests/Source/Main.cpp @@ -0,0 +1,12 @@ +#include + +int main(int argc, char** argv) +{ + if (argc == 1) + { + testing::GTEST_FLAG(filter) = "-PerformanceTests.AllTests"; + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/Tests/Source/RefObject.cpp b/Tests/Source/RefObject.cpp new file mode 100644 index 00000000..db55113d --- /dev/null +++ b/Tests/Source/RefObject.cpp @@ -0,0 +1,312 @@ +#include "TestBase.hpp" + + +struct RefObjectTests : TestBase +{ + +}; + +TEST_F(RefObjectTests, ValueAccess) +{ + using namespace Lua; + Run("result = true"); + ASSERT_TRUE(Result().Is()); + ASSERT_TRUE(Result().To()); + ASSERT_TRUE((bool)Result()); + + Run("result = 4"); + ASSERT_TRUE(Result().Is()); + ASSERT_EQ(Result().To(), 4); + ASSERT_EQ(Result().To(), 4u); + ASSERT_EQ((int)Result(), 4); + ASSERT_FLOAT_EQ(Result().To(), 4.f); + ASSERT_DOUBLE_EQ(Result().To(), 4.0); + ASSERT_DOUBLE_EQ(Result().To(), 4.0); + + + Run("result = {a = 4}"); + ASSERT_TRUE(Result().IsTable()); + ASSERT_TRUE(Result()["a"].Is()); + + GRefObject key{ l, "a" }; + ASSERT_TRUE(Result()[key].Is()); + + + + +} + +TEST_F(RefObjectTests, IteratorTest) +{ + Run("result = {1,3,4,6,7,10}"); + auto result = Result(); + + int arr[]{ 1,3,4,6,7,10 }; + int index = 0; + + for (const auto& [key, value] : result) + { + ASSERT_TRUE(key.Is()); + ASSERT_TRUE(key == index + 1); + ASSERT_TRUE(value.Is()); + ASSERT_TRUE(value == arr[index]); + index++; + } + + + + + // Run("result = {1,3,4,6,7, a = 5, b = 'abc'}"); + +} + +TEST_F(RefObjectTests, CompareTest) +{ + using namespace Lua; + Run("a, b = 2, 3"); + GRefObject a = GRefObject::Global(l, "a"); + GRefObject b = GRefObject::Global(l, "b"); + + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + ASSERT_TRUE(a < b); + ASSERT_FALSE(a > b); + ASSERT_FALSE(a >= b); + ASSERT_TRUE(a <= b); + +} + + +// Access classes tests + +struct MyAccess +{ + static int GetRef(lua_State* l) + { + return luaL_ref(l, LUA_REGISTRYINDEX); + } + + static void Unref(lua_State* l, int ref) + { + luaL_unref(l, LUA_REGISTRYINDEX, ref); + } + + static void PushRef(lua_State* l, int ref) + { + lua_rawgeti(l, LUA_REGISTRYINDEX, ref); + } +}; + +struct MyAccess2 +{ + static int GetRef(lua_State* l) + { + GetTable(l); + lua_pushvalue(l, -2); + int ref = luaL_ref(l, -2); + lua_pop(l, 2); + return ref; + } + + static void Unref(lua_State* l, int ref) + { + GetTable(l); + luaL_unref(l, -1, ref); + lua_pop(l, 1); + } + + static void PushRef(lua_State* l, int ref) + { + GetTable(l); + lua_rawgeti(l, -1, ref); + lua_remove(l, -2); + } +private: + + static void GetTable(lua_State* l) + { + if (!assigned) + AssignTable(l); + lua_getregp(l, GetIndex()); + assert(lua_istable(l, -1)); + } + + static void AssignTable(lua_State* l) + { + lua_newtable(l); + lua_setregp(l, GetIndex()); + assigned = true; + } + + static constexpr const void* GetIndex() + { + return &index; + } + static const char index = 0; + static bool assigned; +}; +bool MyAccess2::assigned = false; + +TEST_F(RefObjectTests, AccessClasses) +{ + using namespace Lua; + + { + RefObject obj{ l }; + GRefObject gobj{ l }; + obj = "Hello"; + gobj = obj; + ASSERT_TRUE(obj == gobj); + } + { + RefObject obj{ l }; + GRefObject gobj{ l }; + obj = "Hello"; + gobj = obj; + ASSERT_TRUE(obj == gobj); + ASSERT_TRUE(obj.Is()); + ASSERT_STREQ(obj.To(), "Hello"); + } + { + RefObject obj{ l }; + GRefObject gobj{ l }; + obj = RefObject::MakeTable(l); + obj["key"] = "Hi"; + gobj = obj["key"]; + ASSERT_TRUE(gobj == obj["key"]); + ASSERT_TRUE(gobj.Is()); + ASSERT_STREQ(gobj.To(), "Hi"); + } +} + + +TEST_F(RefObjectTests, SelfCallTest) +{ + + using namespace Lua; + Run("result = { " + " func = function(self, s, n) " + " local s_ = self.custom_string " + " for i = 1,n do " + " s_ = s_ .. s " + " end " + " return s_ " + " end, " + " custom_string = 'My string', " + "} " + ); + { + auto s = Result().SelfCall("func", "1", 5); + ASSERT_TRUE(s.Is()); + ASSERT_STREQ(s.To(), "My string11111"); + } + { + ASSERT_THROW(Result()["func"].Call("1", 5), Exception); + } + { + auto s = Result()["func"].Call(Result(), "1", 5); + ASSERT_TRUE(s.Is()); + ASSERT_STREQ(s.To(), "My string11111"); + } + +} + + + +// exception tests are here for a moment with gtest bug? + + +struct ExceptionTests : TestBase +{ + +}; + +TEST_F(ExceptionTests, ThrowTests) +{ + Run("result = true"); + ASSERT_THROW(Result().Call(), Lua::Exception); + ASSERT_THROW(Result()["Key"].To(), Lua::Exception); + +} + + +// UserData tests + + +struct UserDataTests : TestBase +{ + +}; + +TEST_F(UserDataTests, PropertyTests) +{ + using namespace Lua; + struct Vector3f + { + float x, y, z; + + Vector3f(float x, float y, float z) :x(x), y(y), z(z) + { + + } + Vector3f() :Vector3f(0, 0, 0) {} + + float Length()const + { + return sqrtf(x * x + y * y + z * z); + } + }; + + Class(l, "Vector") + .AddConstructor, Default, Default>() + .Add("x", Property{}) + .Add("y", Property{}) + .Add("z", Property{}) + .Add("Length", Method{}) + ; + Run("v = Vector(1,2,3) " + "result = v.x " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 1.f); + Run( + "result = v.y " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 2.f); + Run( + "result = v.z " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 3.f); + { + Run("v.x = 2 " + "result = v.y " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 2.f); + Run("v.y = 4 " + "result = v.y " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 4.f); + Run("v.z = 8 " + "result = v.z " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), 8.f); + } + { + ASSERT_THROW(Run("v.w = 2"), std::runtime_error); + Run("result = v.w " + ); + ASSERT_TRUE(Result().Is()); + } + { + Run("v = Vector(1,2,3) " + "result = v:Length() " + ); + ASSERT_TRUE(Result().Is()); + ASSERT_FLOAT_EQ(Result().To(), Vector3f(1, 2, 3).Length()); + } +} \ No newline at end of file diff --git a/Tests/Source/TestBase.hpp b/Tests/Source/TestBase.hpp new file mode 100644 index 00000000..d7962182 --- /dev/null +++ b/Tests/Source/TestBase.hpp @@ -0,0 +1,44 @@ +#pragma once +#include +#include "Lua/LuaLibrary.h" +#include "LuaTemplateLibrary/LTL.hpp" + +struct TestBase : public testing::Test +{ + + lua_State* l = nullptr; + + void SetUp() override + { + l = nullptr; + l = luaL_newstate(); + luaL_openlibs(l); + lua_atpanic(l, Lua::Exception::PanicFunc); + } + + void TearDown() override + { + if (l != nullptr) + { + lua_close(l); + } + } + + void Run(const std::string &s) + { + if (luaL_loadstring(l, s.c_str()) != 0) + { + throw std::runtime_error(lua_tostring(l, -1)); + } + + if (lua_pcall(l, 0, 0, 0) != 0) + { + throw std::runtime_error(lua_tostring(l, -1)); + } + } + + Lua::GRefObject Result() + { + return Lua::GRefObject::Global(l, "result"); + } +}; \ No newline at end of file diff --git a/Tests/main.lua b/Tests/main.lua new file mode 100644 index 00000000..d6590a26 --- /dev/null +++ b/Tests/main.lua @@ -0,0 +1,142 @@ +function Test() + + local t = MakeArray(10) + print(#t) + for i = 1, #t do + print(t[i]) + t[i] = i + end + t = DoubleArray(t) + print(#t) + for i = 1, #t do + print(t[i]) + end + print(Def(3)) + print(Def(3,2)) + print(Upval(3)) + print(Opt(3)) + print(DoubleInt(2, 3)) + print(TripleInt(2, 3, 5)) + + --local tc = TestClass.new() + --tc:print() + --tc:aboba() + PrintInc() + PrintInc() + PrintInc() + print(VectorLen{1,2,3}) + local v = VectorSum({1,2,3},{4,5,6}) + print(table.unpack(v)) + local v = VecSum2({1,2,3},{4,5,6}) + print(table.unpack(v)) + local v = VecPtr({4,5,6}) + print(table.unpack(v)) + print(GetSystemTime()) + SayHello() + SayBye() + SayBye("Pain") + SayFoo() + Say(1) + print(Gamma(0.5)) + print(MyFunc(2, 5)) +end + + +local function MySum( v1, v2) + return {v1[1]+v2[1],v1[2]+v2[2],v1[3]+v2[3]} +end +local Sqrt = math.sqrt +local function MyLen( v) + return Sqrt(v[1]*v[1]+v[2]*v[2]+v[3]*v[3]) +end + + +local function MyLenXYZ( v) + local x,y,z = v[1],v[2],v[3] + return Sqrt(x*x+y*y+z*z) +end + +function TestSum(n) + local VectorSum = VectorSum + local v1 = {1,2,3} + local v2 = {4,5,6} + local start_t = GetSystemTime() + for i = 1,n do + VectorSum(v1,v2) + end + + return GetSystemTime() - start_t +end + +function TestMySum(n) + local VectorSum = MySum + local v1 = {1,2,3} + local v2 = {4,5,6} + local start_t = GetSystemTime() + for i = 1,n do + VectorSum(v1,v2) + end + + return GetSystemTime() - start_t +end + +function TestLen(n) + local VectorLen = VectorLen + local v = {4,5,6} + local start_t = GetSystemTime() + for i = 1,n do + VectorLen(v) + end + + return GetSystemTime() - start_t +end +function TestLenXYZ(n) + local VectorLen = MyLenXYZ + local v = {4,5,6} + local start_t = GetSystemTime() + for i = 1,n do + VectorLen(v) + end + + return GetSystemTime() - start_t +end + +function TestMyLen(n) + local VectorLen = MyLen + local v = {4,5,6} + local start_t = GetSystemTime() + for i = 1,n do + VectorLen(v) + end + + return GetSystemTime() - start_t +end + +function RefFunc(a) + print(tostring(a)) + return a +end + +function Main(obj) + print(tostring(obj.func)) + local t ={Lua = "No"} + print(t.Lua) + obj.func(t) + print(t.Lua) + + -- for k, v in pairs(obj) do + -- print(tostring(k)..":"..tostring(v)) + -- end + --Test() + --local n = 1000000 + --print(TestSum(n)) + --print(TestMySum(n)) + + --print(TestMyLen(n)) + --print(TestLen(n)) + --print(TestLenXYZ(n)) + + + + return n +end diff --git a/googletest b/googletest new file mode 160000 index 00000000..9bb354fa --- /dev/null +++ b/googletest @@ -0,0 +1 @@ +Subproject commit 9bb354fa8325fa31faa1e12627b25ab445e6eed3