├── .travis.yml ├── CHANGES ├── CMakeLists.txt ├── FindLua52.cmake ├── FindLuaJIT51.cmake ├── LICENSE.md ├── README.md ├── include ├── LuaContext.hpp └── misc │ └── exception.hpp └── tests ├── advanced_readwrite.cpp ├── basic_readwrite.cpp ├── custom_types.cpp ├── execution.cpp ├── functions_read.cpp ├── functions_write.cpp ├── main.cpp ├── metatables.cpp ├── movable.cpp └── threads.cpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 7 | - echo "deb http://llvm.org/apt/quantal/ llvm-toolchain-quantal main" | sudo tee -a /etc/apt/sources.list 8 | - wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - 9 | - sudo apt-get -qq update 10 | - sudo apt-get install libboost-dev 11 | - sudo apt-get install $LUA 12 | - 'if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi' 13 | - 'if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi' 14 | - mkdir build 15 | - cd build 16 | - $CXX --version 17 | - cmake -DCMAKE_CXX_FLAGS="-Wall $CXX_FLAGS" .. 18 | script: 19 | - make 20 | - ./tests 21 | env: 22 | - LUA="liblua5.1-dev" 23 | - LUA="liblua5.2-dev" 24 | - LUA="libluajit-5.1-dev" 25 | matrix: 26 | allow_failures: 27 | - compiler: clang 28 | - env: LUA="libluajit-5.1-dev" -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Version 1.2.1 2 | ============= 3 | * Fixed members registration not working with pointers and shared pointers to objects 4 | * Enforced const-correctness: a pointer or a shared pointer to a const object will now only have access to reading members and calling const functions 5 | 6 | Version 1.2 7 | ============= 8 | Major: 9 | * Added support for metatables 10 | * Fixed bug with ref and const-ref callback parameters 11 | * Fixed exceptions not being thrown by writeVariable with Lua 5.2 12 | * Fixed exception-safety when callbacks throw exceptions 13 | 14 | Minor: 15 | * Changed LuaEmptyArray to LuaContext::EmptyArray and LuaMetatable to LuaContext::Metatable (former definitions are working but deprecated) 16 | * Fixed custom object being copied instead of moved when being returned by a callback 17 | * Fixed bug when reading arrays from boost::variant 18 | 19 | Version 1.1 20 | ============= 21 | Major: 22 | * Functions with a varying number of parameters are now supported 23 | * Removed VariableDoesntExistException (this class was a remainder from the old version and was never thrown by anything) 24 | * Attempting to call a function with the wrong parameter types now triggers a Lua error instead of an exception 25 | * Now possible to write and read enums 26 | 27 | Minor: 28 | * Added reading LuaFunctionCaller as a faster alternative to reading functions 29 | * Fixed writing std::vector producing arrays starting at offset 0 30 | * Registered functions are now stored inside the Lua state instead of maps in LuaContext 31 | * Added more assertions and more static assertions 32 | * Fixed exception safety when writing inside tables 33 | * Now using Boost type_traits instead of standard library type_traits 34 | * Writing a function or function object with a trivial destructor now writes a C closure instead of a userdata with metatable 35 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(luawrapper) 3 | 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}) 5 | set(CMAKE_GENERATOR_TOOLSET "v120" CACHE STRING "") # building for VC++ 2013 6 | 7 | 8 | # boost 9 | find_package(Boost REQUIRED) 10 | include_directories(${Boost_INCLUDE_DIRS}) 11 | 12 | # lua 13 | find_package(Lua51) 14 | find_package(Lua52) 15 | find_package(LuaJIT51) 16 | if (NOT ${LUA51_FOUND} AND NOT ${LUA52_FOUND} AND NOT ${LUAJIT51_FOUND}) 17 | message(FATAL_ERROR "Could not find lua library") 18 | endif() 19 | include_directories(${LUA_INCLUDE_DIR}) 20 | 21 | # including lua wrapper 22 | include_directories(include) 23 | 24 | # setting up external project 25 | include(ExternalProject) 26 | 27 | # gtest 28 | find_package(Subversion REQUIRED) 29 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/ThirdParty/googletest-src) 30 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/ThirdParty/googletest-bin) 31 | execute_process(COMMAND ${Subversion_SVN_EXECUTABLE} checkout http://googletest.googlecode.com/svn/trunk/ googletest-src WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/ThirdParty) 32 | add_subdirectory(${CMAKE_BINARY_DIR}/ThirdParty/googletest-src ThirdParty/googletest-bin) 33 | include_directories(${CMAKE_BINARY_DIR}/ThirdParty/googletest-src/include) 34 | 35 | # flags 36 | if (${CMAKE_COMPILER_IS_GNUCXX}) 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 38 | endif() 39 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 40 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 41 | endif() 42 | 43 | # executable 44 | add_executable(tests 45 | tests/main.cpp 46 | tests/basic_readwrite.cpp 47 | tests/advanced_readwrite.cpp 48 | tests/execution.cpp 49 | tests/functions_write.cpp 50 | tests/functions_read.cpp 51 | tests/custom_types.cpp 52 | tests/movable.cpp 53 | tests/metatables.cpp 54 | tests/threads.cpp 55 | ) 56 | 57 | target_link_libraries(tests 58 | ${LUA_LIBRARIES} 59 | gtest 60 | gtest_main 61 | ) 62 | 63 | # hack 64 | if (${UNIX}) 65 | target_link_libraries(tests 66 | pthread 67 | ) 68 | endif() 69 | 70 | enable_testing() 71 | add_test(tests tests) 72 | -------------------------------------------------------------------------------- /FindLua52.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA52_FOUND, if false, do not try to link to Lua 4 | # LUA_LIBRARIES 5 | # LUA_INCLUDE_DIR, where to find lua.h 6 | # 7 | # Note that the expected include convention is 8 | # #include "lua.h" 9 | # and not 10 | # #include 11 | # This is because, the lua location is not standardized and may exist 12 | # in locations other than lua/ 13 | 14 | #============================================================================= 15 | # Copyright 2007-2009 Kitware, Inc. 16 | # 17 | # Distributed under the OSI-approved BSD License (the "License"); 18 | # see accompanying file Copyright.txt for details. 19 | # 20 | # This software is distributed WITHOUT ANY WARRANTY; without even the 21 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 22 | # See the License for more information. 23 | #============================================================================= 24 | # (To distribute this file outside of CMake, substitute the full 25 | # License text for the above reference.) 26 | 27 | FIND_PATH(LUA_INCLUDE_DIR lua.h 28 | HINTS 29 | $ENV{LUA_DIR} 30 | PATH_SUFFIXES include/lua52 include/lua5.2 include/lua include 31 | PATHS 32 | ~/Library/Frameworks 33 | /Library/Frameworks 34 | /usr/local 35 | /usr 36 | /sw # Fink 37 | /opt/local # DarwinPorts 38 | /opt/csw # Blastwave 39 | /opt 40 | ) 41 | 42 | FIND_LIBRARY(LUA_LIBRARY 43 | NAMES lua52 lua5.2 lua-5.2 lua 44 | HINTS 45 | $ENV{LUA_DIR} 46 | PATH_SUFFIXES lib64 lib 47 | PATHS 48 | ~/Library/Frameworks 49 | /Library/Frameworks 50 | /usr/local 51 | /usr 52 | /sw 53 | /opt/local 54 | /opt/csw 55 | /opt 56 | ) 57 | 58 | IF(LUA_LIBRARY) 59 | # include the math library for Unix 60 | IF(UNIX AND NOT APPLE) 61 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 62 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 63 | # For Windows and Mac, don't need to explicitly include the math library 64 | ELSE(UNIX AND NOT APPLE) 65 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 66 | ENDIF(UNIX AND NOT APPLE) 67 | ENDIF(LUA_LIBRARY) 68 | 69 | #INCLUDE(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) 70 | include(FindPackageHandleStandardArgs) 71 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 72 | # all listed variables are TRUE 73 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua52 DEFAULT_MSG LUA_LIBRARIES LUA_INCLUDE_DIR) 74 | 75 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) -------------------------------------------------------------------------------- /FindLuaJIT51.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUAJIT51_FOUND, if false, do not try to link to Lua 4 | # LUA_LIBRARIES 5 | # LUA_INCLUDE_DIR, where to find lua.h 6 | # 7 | # Note that the expected include convention is 8 | # #include "lua.h" 9 | # and not 10 | # #include 11 | # This is because, the lua location is not standardized and may exist 12 | # in locations other than lua/ 13 | 14 | #============================================================================= 15 | # Copyright 2007-2009 Kitware, Inc. 16 | # 17 | # Distributed under the OSI-approved BSD License (the "License"); 18 | # see accompanying file Copyright.txt for details. 19 | # 20 | # This software is distributed WITHOUT ANY WARRANTY; without even the 21 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 22 | # See the License for more information. 23 | #============================================================================= 24 | # (To distribute this file outside of CMake, substitute the full 25 | # License text for the above reference.) 26 | 27 | FIND_PATH(LUA_INCLUDE_DIR luajit.h 28 | HINTS 29 | $ENV{LUA_DIR} 30 | PATH_SUFFIXES include/luajit-2.0 include/luajit include 31 | PATHS 32 | ~/Library/Frameworks 33 | /Library/Frameworks 34 | /usr/local 35 | /usr 36 | /sw # Fink 37 | /opt/local # DarwinPorts 38 | /opt/csw # Blastwave 39 | /opt 40 | ) 41 | 42 | FIND_LIBRARY(LUA_LIBRARY 43 | NAMES luajit-5.1 44 | HINTS 45 | $ENV{LUA_DIR} 46 | PATH_SUFFIXES lib64 lib 47 | PATHS 48 | ~/Library/Frameworks 49 | /Library/Frameworks 50 | /usr/local 51 | /usr 52 | /sw 53 | /opt/local 54 | /opt/csw 55 | /opt 56 | ) 57 | 58 | IF(LUA_LIBRARY) 59 | # include the math library for Unix 60 | IF(UNIX AND NOT APPLE) 61 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 62 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 63 | # For Windows and Mac, don't need to explicitly include the math library 64 | ELSE(UNIX AND NOT APPLE) 65 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 66 | ENDIF(UNIX AND NOT APPLE) 67 | ENDIF(LUA_LIBRARY) 68 | 69 | #INCLUDE(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) 70 | include(FindPackageHandleStandardArgs) 71 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 72 | # all listed variables are TRUE 73 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT51 DEFAULT_MSG LUA_LIBRARIES LUA_INCLUDE_DIR) 74 | 75 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Pierre Krieger 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This fork isn't maintained 2 | 3 | Please see https://github.com/ahupowerdns/luawrapper 4 | 5 | 6 | ## Tomaka's lua wrapper 7 | 8 | [![Build Status](https://travis-ci.org/Tomaka17/luawrapper.svg?branch=master)](https://travis-ci.org/Tomaka17/luawrapper) 9 | 10 | ### What is this library? 11 | This lua wrapper for C++ is a library which allows you to easily manage lua code. It was designed to be as simple as possible to use. 12 | 13 | ### Why this library? 14 | You may wonder why I chose to create my own library while there as so many other libraries available. Well, none of them 100 % satisfied me. 15 | 16 | Some are just basic wrappers (with functions to directly manipulate the stack), some others use an external executable to compute the list of functions of a class, and some others are just too complicated to use. 17 | 18 | This library was designed to be very simple to use: you can write Lua variables (with either a number, a string, a function, an array or any object), read Lua variables, and of course execute Lua code. That's all. 19 | 20 | ### How to use it? 21 | This is a headers-only library. 22 | Simply add the `include` directory to your include paths, and you can use the `LuaContext` class. 23 | You can also just copy-paste the files into your own project if you don't want to modify your include paths. 24 | 25 | The `include/misc/exception.hpp` file is required only for VC++. 26 | 27 | All the files outside of the `include` directory are only for testing purposes and can be ignored. 28 | 29 | ### Why should I use it? 30 | * very easy to use 31 | * no macros (we are in the 21st century!) 32 | * no external program to run on your source code 33 | * not intrusive, you don't need to change the layout of your functions or classes 34 | * you aren't forced to use object-oriented code 35 | * portable (doesn't use anything that is compiler-specific or platform-specific) 36 | * can do everything other libraries can do 37 | 38 | ### Why should I not use it? 39 | * requires support for [C++11](http://en.wikipedia.org/wiki/C%2B%2B11), the latest C++ standard 40 | * requires [Boost](http://boost.org) (headers only) 41 | * inheritance not supported (and won't be supported until some reflection is added to the C++ language) 42 | * uses exceptions and RTTI 43 | 44 | ### I have the old (2010) version of your library, what did change? 45 | * you need an up-to-date compiler now 46 | * you now need some headers-only libraries from [Boost](http://boost.org) 47 | * breaking change: `LuaContext` is no longer in the `Lua` namespace 48 | * breaking change: you can't pass directly lambdas to `writeVariable` anymore, use `writeFunction` instead or convert them to `std::function` 49 | * breaking change: the functions `clearVariable`, `doesVariableExist`, `writeEmptyArray` and `callLuaFunction` no longer exist, but you can reproduce their effect with `writeVariable` and `readVariable` 50 | * a lot of features have been added: lua arrays, polymorphic functions, etc. 51 | * the implementation is really a lot cleaner, and probably faster and with less bugs 52 | 53 | ### Documentation 54 | All the examples below are in C++, except the parameter passed to `executeCode` which is Lua code. 55 | 56 | #### Reading and writing variables 57 | 58 | LuaContext lua; 59 | lua.writeVariable("x", 5); 60 | lua.executeCode("x = x + 2;"); 61 | std::cout << lua.readVariable("x") << std::endl; // prints 7 62 | 63 | Reading and writing global variables of the Lua context can be done with `writeVariable` and `readVariable`. 64 | 65 | All basic language types (`int`, `float`, `bool`, `char`, ...), plus `std::string`, can be read or written. `enum`s can also be read or written but are turned into numbers. 66 | 67 | `readVariable` requires a template parameter which tells the type of the variable that should be read. A `WrongTypeException` is thrown if Lua can't convert the variable to the type you requested, or if you try to read a non-existing variable. 68 | 69 | If you don't know the type of a variable in advance, you can read a `boost::variant`. If you want to read a variable but don't know whether it exists, you can read a `boost::optional`. More informations about this below. 70 | 71 | #### Writing functions 72 | 73 | Writing a function is as easy as writing a value: 74 | 75 | void show(int value) { 76 | std::cout << value << std::endl; 77 | } 78 | 79 | LuaContext lua; 80 | lua.writeVariable("show", &show); 81 | lua.executeCode("show(5)"); // calls the show() function in C++, which prints 5 82 | lua.executeCode("show(7)"); // prints 7 83 | 84 | `writeVariable` also supports `std::function`s: 85 | 86 | std::function f = [](int v) { std::cout << v << std::endl; }; 87 | 88 | LuaContext lua; 89 | lua.writeVariable("show", f); 90 | lua.executeCode("show(3)"); // prints 3 91 | lua.executeCode("show(8)"); // prints 8 92 | 93 | The function's parameters and return type are handled as if they were read and written by `readVariable` and `writeVariable`, which means that all types supported by these functions can also be used as function parameters or return type. 94 | 95 | If some Lua code attempts to call a function with fewer parameters or with parameters of the wrong type, then a Lua error is triggered. 96 | 97 | Converting a lambda function to a `std::function` is costly. Instead you can use `writeFunction`: 98 | 99 | // note: this is C++14 100 | const auto increment = [](auto v) { return v + 1; } 101 | 102 | LuaContext lua; 103 | lua.writeFunction("incrementInt", increment); 104 | lua.writeFunction("incrementDouble", increment); 105 | 106 | If you pass a function object with a single operator(), you can also skip the template parameter of `writeFunction`, in which case the type will be automatically detected. 107 | This is the easiest way to write a lambda. 108 | 109 | LuaContext lua; 110 | lua.writeFunction("show", [](int v) { std::cout << v << std::endl; }); 111 | 112 | 113 | #### Executing code 114 | 115 | Executing Lua code is done with the `executeCode` function. 116 | By default it takes no template parameter, but if the code returns a value, you can pass one to request the specific type to be returned. 117 | 118 | LuaContext lua; 119 | std::cout << lua.executeCode("return 1 + 2") << std::endl; // prints 3 120 | 121 | A `SyntaxErrorException` is thrown in case of a parse error in the code. An `ExecutionErrorException` is thrown in case of an unhandled Lua error during execution. 122 | 123 | `executeCode` also accepts `std::istream` objects. You can easily read lua code (including pre-compiled) from a file like this: 124 | 125 | LuaContext lua; 126 | lua.executeCode(std::ifstream{"script.lua"}); 127 | 128 | If you write your own derivate of `std::istream` (for example a decompressor), you can of course also use it. 129 | Note however that `executeCode` will block until it reaches eof. You should remember this if you use a custom derivate of `std::istream` which awaits for data. 130 | 131 | 132 | #### Exception safety 133 | 134 | You can safely throw exceptions from inside functions called by Lua and they will be turned automatically into Lua errors. 135 | 136 | If the error is not handled by the Lua code, then it will propagate outside of `executeCode`. An `ExecutionErrorException` will be thrown by `executeCode` with the exception thrown by the callback attached as a nested exception. 137 | 138 | lua.writeFunction("test", []() { throw std::runtime_error("Problem"); }); 139 | 140 | try { 141 | lua.executeCode("test()"); 142 | 143 | } catch(const ExecutionErrorException& e) { 144 | std::cout << e.what() << std::endl; // prints an error message 145 | 146 | try { 147 | std::rethrow_if_nested(e); 148 | } catch(const std::runtime_error& e) { 149 | // e is the exception that was thrown from inside the lambda 150 | std::cout << e.what() << std::endl; // prints "Problem" 151 | } 152 | } 153 | 154 | 155 | #### Writing custom types 156 | 157 | class Object { 158 | public: 159 | Object() : value(10) {} 160 | 161 | void increment() { std::cout << "incrementing" << std::endl; value++; } 162 | 163 | int value; 164 | }; 165 | 166 | LuaContext lua; 167 | lua.registerFunction("increment", &Object::increment); 168 | 169 | lua.writeVariable("obj", Object{}); 170 | lua.executeCode("obj:increment();"); 171 | 172 | std::cout << lua.readVariable("obj").value << std::endl; 173 | 174 | Prints `incrementing` and `11`. 175 | 176 | In addition to basic types and functions, you can also pass any object to `writeVariable`. The object will be moved into Lua by calling its copy or move constructor. 177 | 178 | Remember that since they are not a native type, you can't clone an object from within Lua. Attempting to copy the object into another variable will instead make the two variables point to the same object. 179 | 180 | If you want to call an object's member function, you must register it with `registerFunction`, just like in the example above. 181 | It doesn't matter whether you call `registerFunction` before or after writing the objects, it works in both cases. 182 | 183 | If you pass a plain object type as template parameter to `readVariable` (for example `readVariable`, juste like in the code above), then it will read a copy of the object. However if you pass a reference (for example `readVariable`), then a reference to the object held by Lua will be returned. 184 | 185 | You also have the possibility to write and read pointers instead of plain objects. Raw pointers, `unique_ptr`s and `shared_ptr`s are also supported (`unique_ptr`s can't be read for obvious reasons). Functions that have been registered for a type also work if you write pointers to this type. 186 | 187 | Note however that inheritance is not supported. 188 | You need to register all of a type's functions, even if you have already registered the functions of its parents. You can't write an object and attempt to read a reference to its parent type either, this would trigger an exception. 189 | 190 | 191 | #### Executing Lua functions 192 | 193 | LuaContext lua; 194 | 195 | lua.executeCode("foo = function(i) return i + 2 end"); 196 | 197 | const auto function = lua.readVariable>("foo"); 198 | std::cout << function(3) << std::endl; 199 | 200 | Prints `5`. 201 | 202 | `readVariable` also supports `std::function`. This allows you to read any function, even the functions created by lua. 203 | Note however that calling the function after the LuaContext has been destroyed leads to undefined behavior (and likely to a crash), even when the function was originally a C++ function. 204 | 205 | If you want to get maximum performances, you can also ask `readVariable` to read a `LuaContext::LuaFunctionCaller` instead of a `std::function`. 206 | When `readVariable` returns a `std::function`, in fact it is just a wrapping around a `LuaFunctionCaller`. 207 | 208 | 209 | #### Polymorphic functions 210 | 211 | If you want to read a value but don't know in advance whether it is of type A or type B, `writeVariable` and `readVariable` also support `boost::variant`. 212 | 213 | LuaContext lua; 214 | 215 | auto value = lua.readVariable>("value"); 216 | 217 | if (const auto strValue = boost::get(&value)) 218 | ... 219 | else if (const auto boolValue = boost::get(&value)) 220 | ... 221 | 222 | This can be used to create polymorphic functions, ie. functions that can take different types of arguments. 223 | 224 | LuaContext lua; 225 | 226 | lua.writeFunction("foo", 227 | [](boost::variant value) 228 | { 229 | if (value.which() == 0) { 230 | std::cout << "Value is a string: " << boost::get(value); 231 | } else { 232 | std::cout << "Value is a bool: " << boost::get(value); 233 | } 234 | } 235 | ); 236 | 237 | lua.executeCode("foo(\"hello\")"); // prints "Value is a string: hello" 238 | lua.executeCode("foo(true)"); // prints "Value is a bool true" 239 | 240 | See the documentation of [`boost::variant`](http://www.boost.org/doc/libs/release/doc/html/variant.html) for more informations. 241 | 242 | 243 | #### Variadic-like functions 244 | 245 | If you want functions that take a varying number of parameters, you can have some parameters as `boost::optional`s. 246 | 247 | LuaContext lua; 248 | 249 | lua.writeFunction("foo", 250 | [](int param1, boost::optional param2, boost::optional param3) 251 | { 252 | if (param3) { 253 | std::cout << "3 parameters" << std::endl; 254 | } else if (param2) { 255 | std::cout << "2 parameters" << std::endl; 256 | } else { 257 | std::cout << "1 parameter" << std::endl; 258 | } 259 | } 260 | ); 261 | 262 | lua.executeCode("foo(7)"); 263 | lua.executeCode("foo(7, 7)"); 264 | lua.executeCode("foo(7, 7, 7)"); 265 | 266 | Just like C/C++ functions with default parameter values, you have to put the `boost::optional`s at the end of the parameters list. 267 | 268 | This means that for example: 269 | 270 | lua.writeFunction("foo", 271 | [](boost::optional param1, int param2) {} 272 | ); 273 | 274 | lua.executeCode("foo(7)"); 275 | 276 | This code will trigger a Lua error because the `foo` function requires at least two parameters. 277 | 278 | 279 | #### Writing and reading arrays 280 | 281 | `writeVariable` and `readVariable` can also read of write associative arrays in the form of `std::vector` of `std::pair`s, `std::map` and `std::unordered_map`. For `std::vector` which contains `std::pair`s, the first member of the pair is the key and the second member is the value. 282 | 283 | LuaContext lua; 284 | 285 | lua.writeVariable("a", 286 | std::unordered_map 287 | { 288 | { 12, "hello" }, 289 | { 794, "goodbye" }, 290 | { 4, "how are" }, 291 | { 40, "you" } 292 | } 293 | ); 294 | 295 | std::cout << lua.executeCode("return a[40]") << std::endl; // prints "you" 296 | 297 | Remember that Lua arrays start at offset 1. When you read a `std::map` from a table created in Lua like this `a = { 5, 12 }`, then the first key is 1. 298 | 299 | You can combine this with `boost::variant`, which allows you to write polymorphic arrays. 300 | 301 | LuaContext lua; 302 | 303 | lua.writeVariable("a", 304 | std::vector< std::pair< boost::variant, boost::variant > > 305 | { 306 | { "test", true }, 307 | { 2, 6.4f }, 308 | { "hello", 1.f }, 309 | { "world", -7.6f }, 310 | { 18, false } 311 | } 312 | ); 313 | 314 | std::cout << lua.executeCode("return a.test") << std::endl; // prints "true" 315 | std::cout << lua.executeCode("return a[2]") << std::endl; // prints "6.4" 316 | 317 | Also note that you can create recursive variants, so you can read arrays which contain arrays which contain arrays, and so forth. 318 | 319 | typedef typename boost::make_recursive_variant 320 | < 321 | std::string, 322 | double, 323 | bool, 324 | std::vector, 326 | boost::recursive_variant_ 327 | >> 328 | >::type 329 | AnyValue; 330 | 331 | lua.readVariable("something"); 332 | 333 | 334 | #### Writing and reading inside arrays 335 | 336 | You can also use `readVariable`, `writeVariable` and `writeFunction` to directly read or write inside an array. Again, remember that the first offset of a Lua array is 1. 337 | 338 | std::cout << lua.readVariable("a", "test") << std::endl; // reads the offset "test" of the array "a" 339 | std::cout << lua.writeVariable("a", 2, true) << std::endl; // writes "true" at the offset "2" of the array "a" 340 | 341 | You can also write an empty array, like this: 342 | 343 | LuaContext lua; 344 | lua.writeVariable("a", LuaContext::EmptyArray); 345 | 346 | Trying to write `LuaContext::EmptyArray` in a Lua variable instead writes an empty array. 347 | 348 | 349 | #### Metatables 350 | 351 | You can read or write the metatable of an object with `readVariable`, `writeVariable` or `writeFunction` as if it was an array, using the special `LuaContext::Metatable` index. 352 | The metatable is automatically created if it doesn't exist. 353 | 354 | struct Foo {}; 355 | 356 | LuaContext lua; 357 | lua.writeVariable("foo", Foo{}); 358 | lua.writeFunction("foo", LuaContext::Metatable, "__call", [](Foo&) { }); 359 | lua.executeCode("foo()"); // calls the lambda above 360 | 361 | Note that functions and custom objects written by this library work thanks to their metatable. 362 | Modifying the metatable of a custom object can break it, especially modifying the `__gc` entry can lead to a memory leak. 363 | 364 | 365 | #### Returning multiple values 366 | 367 | LuaContext lua; 368 | lua.writeFunction("f1", [](int a, int b, int c) { return std::make_tuple(a + b + c, "test"); }); 369 | 370 | lua.executeCode("a, b = f1(1, 2, 3);"); 371 | 372 | std::cout << lua.readVariable("a") << std::endl; 373 | std::cout << lua.readVariable("b") << std::endl; 374 | 375 | Prints `6` and `test`. 376 | 377 | Lua supports functions that return multiple values at once. A C++ function can do so by returning a tuple. 378 | In this example we return at the same time an int and a string. 379 | 380 | Tuples are only supported when returning as a return value for a function. Attempting to write or read a tuple with `writeVariable` or `readVariable` would lead to a compilation error. 381 | 382 | 383 | #### Destroying a Lua variable 384 | 385 | LuaContext lua; 386 | 387 | lua.writeVariable("a", 2); 388 | 389 | lua.writeVariable("a", nullptr); // destroys a 390 | 391 | The C++ equivalent for `nil` is `nullptr`. 392 | 393 | Note that `nullptr` has its own type, which is different from `0` and `NULL`. 394 | 395 | 396 | #### Custom member functions 397 | 398 | In example 3, we saw that you can register functions for a given type with `registerFunction`. 399 | 400 | But you can also register functions that don't exist in the class. 401 | 402 | struct Foo { int value; }; 403 | 404 | LuaContext lua; 405 | 406 | lua.registerFunction("add", [](Foo& object, int num) { object.value += num; }); 407 | 408 | lua.writeVariable("a", Foo{5}); 409 | lua.executeCode("a:add(3)"); 410 | 411 | std::cout << lua.readVariable("a").value; // 8 412 | 413 | The template parameter must be a pointer-to-member-function type. The callback must take as first parameter a reference to the object. 414 | 415 | There is an alternative syntax if you want to register a function for a pointer type, because it is illegal to write `void (Foo*::*)(int)`. 416 | 417 | lua.registerFunction("add", [](Foo& object, int num) { object.value += num; }); 418 | 419 | 420 | #### Member objects and custom member objects 421 | 422 | You can also register member variables for objects. 423 | 424 | struct Foo { int value; }; 425 | 426 | LuaContext lua; 427 | 428 | lua.registerMember("value", &Foo::value); 429 | 430 | lua.writeVariable("a", Foo{}); 431 | 432 | lua.executeCode("a.value = 14"); 433 | std::cout << lua.readVariable("a").value; // 14 434 | 435 | Just like `registerFunction`, you can register virtual member variables. 436 | 437 | The second parameter is a function or function object that is called in order to read the value of the variable. 438 | The third parameter is a function or function object that is called in order to write the value. The third parameter is optional. 439 | 440 | lua.registerMember("value_plus_one", 441 | [](Foo& object) -> int { 442 | // called when lua code wants to read the variable 443 | return object.value + 1; 444 | }, 445 | [](Foo& object, int value_plus_one) { 446 | // called when lua code wants to modify the variable 447 | object.value = value_plus_one - 1; 448 | } 449 | ); 450 | 451 | lua.writeVariable("a", Foo{8}); 452 | std::cout << lua.executeCode("return a.value_plus_one"); // 9 453 | 454 | lua.writeVariable("b", Foo{1}); 455 | lua.executeCode("b.value_plus_one = 5"); 456 | std::cout << lua.readVariable("b").value; // 4 457 | 458 | The alternative syntax also exists. 459 | 460 | lua.registerMember("value_plus_one", ...same as above...); 461 | 462 | Finally you can register functions that will be called by default when a non-existing variable is read or written. 463 | The syntax is the same than above, except that the callbacks take an extra `name` parameter. 464 | 465 | lua.registerMember( 466 | [](Foo& object, const std::string& memberName) -> int { 467 | std::cout << "Trying to read member " << memberName << " of object" << std::endl; 468 | return 1; 469 | }, 470 | [](Foo& object, const std::string& memberName, int value) { 471 | std::cout << "Trying to write member " << memberName << " of object with value " << value << std::endl; 472 | } 473 | ); 474 | 475 | Remember that you can return `std::function` from the read callback, allowing you to create real virtual objects. 476 | 477 | 478 | ### Clean assembly generation 479 | 480 | This library is heavily-templated, which means that it may take additional time to compile but will generate clean assembly code. 481 | 482 | For example this: 483 | 484 | writeVariable("test", "a", 12); 485 | 486 | Will generate something like this: 487 | 488 | movl %ebx, (%esp) 489 | movl $2, 8(%esp) 490 | movl $-1001000, 4(%esp) 491 | call lua_rawgeti 492 | movl %ebx, (%esp) 493 | movl $.LC0, 4(%esp) # contains "test" 494 | call lua_pushstring 495 | movl %ebx, (%esp) 496 | movl $-2, 4(%esp) 497 | call lua_gettable 498 | movl %ebx, (%esp) 499 | movl $12, 4(%esp) 500 | call lua_pushinteger 501 | movl %ebx, (%esp) 502 | movl $.LC1, 8(%esp) # contains "a" 503 | movl $-2, 4(%esp) 504 | call lua_setfield 505 | movl %ebx, (%esp) 506 | movl $-3, 4(%esp) 507 | call lua_settop 508 | 509 | Another example. This code: 510 | 511 | lua.writeFunction("foo", [](int a) { return a + 1; }); 512 | 513 | Will generate this in the main function: 514 | 515 | movl %ebx, (%esp) 516 | movl $_ZZ4mainENUliE_4_FUNEi, 4(%esp) 517 | call lua_pushlightuserdata 518 | movl %ebx, (%esp) 519 | movl $1, 8(%esp) 520 | movl $_ZZN10LuaContext6PusherIFiiEvE4pushEP9lua_StatePS1_ENUlS4_E_4_FUNES4_, 4(%esp) 521 | call lua_pushcclosure 522 | movl %ebx, (%esp) 523 | movl $.LC3, 4(%esp) 524 | call lua_setglobal 525 | 526 | And this helper function: 527 | 528 | pushl %ebp 529 | movl %esp, %ebp 530 | pushl %edi 531 | pushl %esi 532 | pushl %ebx 533 | subl $60, %esp 534 | movl 8(%ebp), %ebx 535 | movl $-1001001, 4(%esp) 536 | movl %ebx, (%esp) 537 | call lua_touserdata 538 | movl %ebx, (%esp) 539 | call lua_gettop 540 | testl %eax, %eax 541 | jle .L458 # handling not enough parameters 542 | cmpl $1, %eax 543 | jne .L459 # handling too many parameters 544 | leal -36(%ebp), %esi 545 | movl %esi, 8(%esp) 546 | movl $-1, 4(%esp) 547 | movl %ebx, (%esp) 548 | call lua_tointegerx 549 | movl -36(%ebp), %ecx 550 | testl %ecx, %ecx 551 | je .L436 # handling wrong parameter type 552 | addl $1, %eax 553 | movl %eax, 4(%esp) 554 | movl %ebx, (%esp) 555 | call lua_pushinteger 556 | leal -12(%ebp), %esp 557 | movl $1, %eax 558 | popl %ebx 559 | popl %esi 560 | popl %edi 561 | popl %ebp 562 | ret 563 | 564 | (generated with `g++-4.8 -O3 -NDEBUG -std=c++11`) 565 | 566 | 567 | ### Compilation 568 | This code uses new functionalities from [C++11](http://en.wikipedia.org/wiki/C%2B%2B11). 569 | 570 | [![build status](https://secure.travis-ci.org/Tomaka17/luawrapper.png)](http://travis-ci.org/Tomaka17/luawrapper) 571 | 572 | Does it compile on: 573 | * Visual C++ 2012 or below: no 574 | * Visual C++ 2013: no 575 | * Visual C++ 2013 with november CTP: yes 576 | * gcc 4.7.2: doesn't always compile because of known bug in the compiler 577 | * gcc 4.8.1: yes 578 | * clang 3.2: doesn't always compile because of known bug in the compiler 579 | * clang 3.5: yes 580 | -------------------------------------------------------------------------------- /include/LuaContext.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Pierre KRIEGER 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef INCLUDE_LUACONTEXT_HPP 29 | #define INCLUDE_LUACONTEXT_HPP 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #ifdef _MSC_VER 58 | # include "misc/exception.hpp" 59 | #endif 60 | 61 | #ifdef __GNUC__ 62 | # define ATTR_UNUSED __attribute__((unused)) 63 | #else 64 | # define ATTR_UNUSED 65 | #endif 66 | 67 | /** 68 | * Defines a Lua context 69 | * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions), 70 | * we only provide few functions like readVariable and writeVariable. 71 | * 72 | * You can also write variables with C++ functions so that they are callable by Lua. Note however that you HAVE TO convert 73 | * your function to std::function (not directly std::bind or a lambda function) so the class can detect which argument types 74 | * it wants. These arguments may only be of basic types (int, float, etc.) or std::string. 75 | */ 76 | class LuaContext { 77 | struct ValueInRegistry; 78 | template struct Binder; 79 | template struct IsOptional; 80 | enum Globals_t { Globals }; // tag for "global variables" 81 | public: 82 | /** 83 | * @param openDefaultLibs True if luaL_openlibs should be called 84 | */ 85 | explicit LuaContext(bool openDefaultLibs = true) 86 | { 87 | // luaL_newstate can return null if allocation failed 88 | mState = luaL_newstate(); 89 | if (mState == nullptr) 90 | throw std::bad_alloc(); 91 | 92 | // setting the panic function 93 | lua_atpanic(mState, [](lua_State* state) -> int { 94 | const std::string str = lua_tostring(state, -1); 95 | lua_pop(state, 1); 96 | assert(false && "lua_atpanic triggered"); 97 | exit(0); 98 | }); 99 | 100 | // opening default library if required to do so 101 | if (openDefaultLibs) 102 | luaL_openlibs(mState); 103 | } 104 | 105 | /** 106 | * Move constructor 107 | */ 108 | LuaContext(LuaContext&& s) : 109 | mState(s.mState) 110 | { 111 | s.mState = luaL_newstate(); 112 | } 113 | 114 | /** 115 | * Move operator 116 | */ 117 | LuaContext& operator=(LuaContext&& s) noexcept 118 | { 119 | std::swap(mState, s.mState); 120 | return *this; 121 | } 122 | 123 | /** 124 | * Copy is forbidden 125 | */ 126 | LuaContext(const LuaContext&) = delete; 127 | 128 | /** 129 | * Copy is forbidden 130 | */ 131 | LuaContext& operator=(const LuaContext&) = delete; 132 | 133 | /** 134 | * Destructor 135 | */ 136 | ~LuaContext() noexcept 137 | { 138 | assert(mState); 139 | lua_close(mState); 140 | } 141 | 142 | /** 143 | * Thrown when an error happens during execution of lua code (like not enough parameters for a function) 144 | */ 145 | class ExecutionErrorException : public std::runtime_error 146 | { 147 | public: 148 | ExecutionErrorException(const std::string& msg) : 149 | std::runtime_error(msg) 150 | { 151 | } 152 | }; 153 | 154 | /** 155 | * Thrown when a syntax error happens in a lua script 156 | */ 157 | class SyntaxErrorException : public std::runtime_error 158 | { 159 | public: 160 | SyntaxErrorException(const std::string& msg) : 161 | std::runtime_error(msg) 162 | { 163 | } 164 | }; 165 | 166 | /** 167 | * Thrown when trying to cast a Lua variable to an unvalid type, eg. trying to read a number when the variable is a string 168 | */ 169 | class WrongTypeException : public std::runtime_error 170 | { 171 | public: 172 | WrongTypeException(std::string luaType, const std::type_info& destination) : 173 | std::runtime_error("Trying to cast a lua variable from \"" + luaType + "\" to \"" + destination.name() + "\""), 174 | luaType(luaType), 175 | destination(destination) 176 | { 177 | } 178 | 179 | std::string luaType; 180 | const std::type_info& destination; 181 | }; 182 | 183 | /** 184 | * Function object that can call a function stored by Lua 185 | * This type is copiable and movable, but not constructible. It can only be created through readVariable. 186 | * @tparam TFunctionType Function type (eg. "int (int, bool)") 187 | */ 188 | template 189 | class LuaFunctionCaller; 190 | 191 | /** 192 | * Opaque type that identifies a Lua thread 193 | */ 194 | struct ThreadID { 195 | ThreadID() = default; 196 | ThreadID(ThreadID&& o) : state(o.state), threadInRegistry(std::move(o.threadInRegistry)) { } 197 | ThreadID& operator=(ThreadID&& o) { std::swap(state, o.state); std::swap(threadInRegistry, o.threadInRegistry); return *this; } 198 | public: 199 | friend LuaContext; 200 | lua_State* state; 201 | std::unique_ptr threadInRegistry; 202 | }; 203 | 204 | /** 205 | * Type that is considered as an empty array 206 | */ 207 | enum EmptyArray_t { EmptyArray }; 208 | 209 | /** 210 | * Type for a metatable 211 | */ 212 | enum Metatable_t { Metatable }; 213 | 214 | /** 215 | * Executes lua code from the stream 216 | * @param code A stream that Lua will read its code from 217 | */ 218 | void executeCode(std::istream& code) 219 | { 220 | auto toCall = load(mState, code); 221 | call>(mState, std::move(toCall)); 222 | } 223 | 224 | /** 225 | * Executes lua code from the stream and returns a value 226 | * @param code A stream that Lua will read its code from 227 | * @tparam TType The type that the executing code should return 228 | */ 229 | template 230 | auto executeCode(std::istream& code) 231 | -> TType 232 | { 233 | auto toCall = load(mState, code); 234 | return call(mState, std::move(toCall)); 235 | } 236 | 237 | /** 238 | * Executes lua code given as parameter 239 | * @param code A string containing code that will be executed by Lua 240 | */ 241 | void executeCode(const std::string& code) 242 | { 243 | executeCode(code.c_str()); 244 | } 245 | 246 | /* 247 | * Executes Lua code from the stream and returns a value 248 | * @param code A string containing code that will be executed by Lua 249 | * @tparam TType The type that the executing code should return 250 | */ 251 | template 252 | auto executeCode(const std::string& code) 253 | -> TType 254 | { 255 | return executeCode(code.c_str()); 256 | } 257 | 258 | /** 259 | * Executes Lua code 260 | * @param code A string containing code that will be executed by Lua 261 | */ 262 | void executeCode(const char* code) 263 | { 264 | auto toCall = load(mState, code); 265 | call>(mState, std::move(toCall)); 266 | } 267 | 268 | /* 269 | * Executes Lua code from the stream and returns a value 270 | * @param code A string containing code that will be executed by Lua 271 | * @tparam TType The type that the executing code should return 272 | */ 273 | template 274 | auto executeCode(const char* code) 275 | -> TType 276 | { 277 | auto toCall = load(mState, code); 278 | return call(mState, std::move(toCall)); 279 | } 280 | 281 | /** 282 | * Executes lua code from the stream 283 | * @param code A stream that Lua will read its code from 284 | */ 285 | void executeCode(const ThreadID& thread, std::istream& code) 286 | { 287 | auto toCall = load(thread.state, code); 288 | call>(thread.state, std::move(toCall)); 289 | } 290 | 291 | /** 292 | * Executes lua code from the stream and returns a value 293 | * @param code A stream that Lua will read its code from 294 | * @tparam TType The type that the executing code should return 295 | */ 296 | template 297 | auto executeCode(const ThreadID& thread, std::istream& code) 298 | -> TType 299 | { 300 | auto toCall = load(thread.state, code); 301 | return call(thread.state, std::move(toCall)); 302 | } 303 | 304 | /** 305 | * Executes lua code given as parameter 306 | * @param code A string containing code that will be executed by Lua 307 | */ 308 | void executeCode(const ThreadID& thread, const std::string& code) 309 | { 310 | executeCode(thread, code.c_str()); 311 | } 312 | 313 | /* 314 | * Executes Lua code from the stream and returns a value 315 | * @param code A string containing code that will be executed by Lua 316 | * @tparam TType The type that the executing code should return 317 | */ 318 | template 319 | auto executeCode(const ThreadID& thread, const std::string& code) 320 | -> TType 321 | { 322 | return executeCode(thread, code.c_str()); 323 | } 324 | 325 | /** 326 | * Executes Lua code 327 | * @param code A string containing code that will be executed by Lua 328 | */ 329 | void executeCode(const ThreadID& thread, const char* code) 330 | { 331 | auto toCall = load(thread.state, code); 332 | call>(thread.state, std::move(toCall)); 333 | } 334 | 335 | /* 336 | * Executes Lua code from the stream and returns a value 337 | * @param code A string containing code that will be executed by Lua 338 | * @tparam TType The type that the executing code should return 339 | */ 340 | template 341 | auto executeCode(const ThreadID& thread, const char* code) 342 | -> TType 343 | { 344 | auto toCall = load(thread.state, code); 345 | return call(thread.state, std::move(toCall)); 346 | } 347 | 348 | /** 349 | * Tells that Lua will be allowed to access an object's function 350 | * This is the version "registerFunction(name, &Foo::function)" 351 | */ 352 | template 353 | auto registerFunction(const std::string& name, TPointerToMemberFunction pointer) 354 | -> typename std::enable_if::value>::type 355 | { 356 | registerFunctionImpl(name, std::mem_fn(pointer), tag{}); 357 | } 358 | 359 | /** 360 | * Tells that Lua will be allowed to access an object's function 361 | * This is the version with an explicit template parameter: "registerFunction(name, [](Foo&) { })" 362 | * @param fn Function object which takes as first parameter a reference to the object 363 | * @tparam TFunctionType Pointer-to-member function type 364 | */ 365 | template 366 | void registerFunction(const std::string& functionName, TType fn) 367 | { 368 | static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); 369 | registerFunctionImpl(functionName, std::move(fn), tag{}); 370 | } 371 | 372 | /** 373 | * Tells that Lua will be allowed to access an object's function 374 | * This is the alternative version with an explicit template parameter: "registerFunction(name, [](Foo&) { })" 375 | * @param fn Function object which takes as first parameter a reference to the object 376 | * @tparam TObject Object to register this function to 377 | * @tparam TFunctionType Function type 378 | */ 379 | template 380 | void registerFunction(const std::string& functionName, TType fn) 381 | { 382 | static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); 383 | registerFunctionImpl(functionName, std::move(fn), tag{}, tag{}); 384 | } 385 | 386 | /** 387 | * Inverse operation of registerFunction 388 | * @tparam TType Type whose function belongs to 389 | */ 390 | template 391 | void unregisterFunction(const std::string& functionName) 392 | { 393 | lua_pushlightuserdata(mState, const_cast(&typeid(TType))); 394 | lua_pushnil(mState); 395 | lua_settable(mState, LUA_REGISTRYINDEX); 396 | checkTypeRegistration(mState, &typeid(TType)); 397 | 398 | lua_pushlightuserdata(mState, const_cast(&typeid(TType*))); 399 | lua_pushnil(mState); 400 | lua_settable(mState, LUA_REGISTRYINDEX); 401 | checkTypeRegistration(mState, &typeid(TType*)); 402 | 403 | lua_pushlightuserdata(mState, const_cast(&typeid(std::shared_ptr))); 404 | lua_pushnil(mState); 405 | lua_settable(mState, LUA_REGISTRYINDEX); 406 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 407 | } 408 | 409 | /** 410 | * Registers a member variable 411 | * This is the version "registerMember(name, &Foo::member)" 412 | */ 413 | template 414 | void registerMember(const std::string& name, TVarType TObject::*member) 415 | { 416 | // implementation simply calls the custom member with getter and setter 417 | const auto getter = [=](const TObject& obj) -> TVarType { return obj.*member; }; 418 | const auto setter = [=](TObject& obj, const TVarType& value) { obj.*member = value; }; 419 | registerMember(name, getter, setter); 420 | } 421 | 422 | /** 423 | * Registers a member variable 424 | * This is the version "registerMember(name, getter, setter)" 425 | * @tparam TObject Type to register the member to 426 | * @tparam TVarType Type of the member 427 | * @param name Name of the member to register 428 | * @param readFunction Function of type "TVarType (const TObject&)" 429 | * @param writeFunction Function of type "void (TObject&, const TVarType&)" 430 | */ 431 | template 432 | void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction) 433 | { 434 | registerMemberImpl(name, std::move(readFunction), std::move(writeFunction)); 435 | } 436 | 437 | /** 438 | * Registers a member variable 439 | * This is the version "registerMember(name, getter, setter)" 440 | * @tparam TMemberType Pointer to member object representing the type 441 | * @param name Name of the member to register 442 | * @param readFunction Function of type "TVarType (const TObject&)" 443 | * @param writeFunction Function of type "void (TObject&, const TVarType&)" 444 | */ 445 | template 446 | void registerMember(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction) 447 | { 448 | static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); 449 | registerMemberImpl(tag{}, name, std::move(readFunction), std::move(writeFunction)); 450 | } 451 | 452 | /** 453 | * Registers a non-modifiable member variable 454 | * This is the version "registerMember(name, getter)" 455 | * @tparam TObject Type to register the member to 456 | * @tparam TVarType Type of the member 457 | * @param name Name of the member to register 458 | * @param readFunction Function of type "TVarType (const TObject&)" 459 | */ 460 | template 461 | void registerMember(const std::string& name, TReadFunction readFunction) 462 | { 463 | registerMemberImpl(name, std::move(readFunction)); 464 | } 465 | 466 | /** 467 | * Registers a non-modifiable member variable 468 | * This is the version "registerMember(name, getter)" 469 | * @tparam TMemberType Pointer to member object representing the type 470 | * @param name Name of the member to register 471 | * @param readFunction Function of type "TVarType (const TObject&)" 472 | */ 473 | template 474 | void registerMember(const std::string& name, TReadFunction readFunction) 475 | { 476 | static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); 477 | registerMemberImpl(tag{}, name, std::move(readFunction)); 478 | } 479 | 480 | /** 481 | * Registers a dynamic member variable 482 | * This is the version "registerMember(getter, setter)" 483 | * @tparam TObject Type to register the member to 484 | * @tparam TVarType Type of the member 485 | * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" 486 | * @param writeFunction Function of type "void (TObject&, const std::string&, const TVarType&)" 487 | */ 488 | template 489 | void registerMember(TReadFunction readFunction, TWriteFunction writeFunction) 490 | { 491 | registerMemberImpl(std::move(readFunction), std::move(writeFunction)); 492 | } 493 | 494 | /** 495 | * Registers a dynamic member variable 496 | * This is the version "registerMember(getter, setter)" 497 | * @tparam TMemberType Pointer to member object representing the type 498 | * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" 499 | * @param writeFunction Function of type "void (TObject&, const std::string&, const TVarType&)" 500 | */ 501 | template 502 | void registerMember(TReadFunction readFunction, TWriteFunction writeFunction) 503 | { 504 | static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); 505 | registerMemberImpl(tag{}, std::move(readFunction), std::move(writeFunction)); 506 | } 507 | 508 | /** 509 | * Registers a dynamic non-modifiable member variable 510 | * This is the version "registerMember(getter)" 511 | * @tparam TObject Type to register the member to 512 | * @tparam TVarType Type of the member 513 | * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" 514 | */ 515 | template 516 | void registerMember(TReadFunction readFunction) 517 | { 518 | registerMemberImpl(std::move(readFunction)); 519 | } 520 | 521 | /** 522 | * Registers a dynamic non-modifiable member variable 523 | * This is the version "registerMember(getter)" 524 | * @tparam TMemberType Pointer to member object representing the type 525 | * @param readFunction Function of type "TVarType (const TObject&, const std::string&)" 526 | */ 527 | template 528 | void registerMember(TReadFunction readFunction) 529 | { 530 | static_assert(std::is_member_object_pointer::value, "registerMember must take a member object pointer type as template parameter"); 531 | registerMemberImpl(tag{}, std::move(readFunction)); 532 | } 533 | 534 | /** 535 | * Creates a new thread 536 | * A Lua thread is not really a thread, but rather an "execution stack". 537 | * You can destroy the thread by calling destroyThread 538 | * @sa destroyThread 539 | */ 540 | auto createThread() 541 | -> ThreadID 542 | { 543 | ThreadID result; 544 | 545 | result.state = lua_newthread(mState); 546 | result.threadInRegistry = std::unique_ptr(new ValueInRegistry(mState)); 547 | lua_pop(mState, 1); 548 | 549 | return std::move(result); 550 | } 551 | 552 | /** 553 | * Destroys a thread created with createThread 554 | * @sa createThread 555 | */ 556 | void destroyThread(ThreadID& id) 557 | { 558 | id.threadInRegistry.reset(); 559 | } 560 | 561 | /** 562 | * Reads the content of a Lua variable 563 | * 564 | * @tparam TType Type requested for the read 565 | * @throw WrongTypeException When the variable is not convertible to the requested type 566 | * @sa writeVariable 567 | * 568 | * Readable types are all types accepted by writeVariable except nullptr, std::unique_ptr and function pointers 569 | * Additionaly supported: 570 | * - LuaFunctionCaller, which is an alternative to std::function 571 | * - references to custom objects, in which case it will return the object in-place 572 | * 573 | * After the variable name, you can add other parameters. 574 | * If the variable is an array, it will instead get the element of that array whose offset is the second parameter. 575 | * Same applies for third, fourth, etc. parameters. 576 | */ 577 | template 578 | TType readVariable(const std::string& name, TTypes&&... elements) const 579 | { 580 | lua_getglobal(mState, name.c_str()); 581 | lookIntoStackTop(mState, std::forward(elements)...); 582 | return readTopAndPop(mState, PushedObject{mState, 1}); 583 | } 584 | 585 | /** 586 | * @sa readVariable 587 | */ 588 | template 589 | TType readVariable(const char* name, TTypes&&... elements) const 590 | { 591 | lua_getglobal(mState, name); 592 | lookIntoStackTop(mState, std::forward(elements)...); 593 | return readTopAndPop(mState, PushedObject{mState, 1}); 594 | } 595 | 596 | /** 597 | * @sa readVariable 598 | */ 599 | template 600 | TType readVariable(const ThreadID& thread, const std::string& name, TTypes&&... elements) const 601 | { 602 | lua_getglobal(thread.state, name.c_str()); 603 | lookIntoStackTop(thread.state, std::forward(elements)...); 604 | return readTopAndPop(thread.state, PushedObject{thread.state, 1}); 605 | } 606 | 607 | /** 608 | * @sa readVariable 609 | */ 610 | template 611 | TType readVariable(const ThreadID& thread, const char* name, TTypes&&... elements) const 612 | { 613 | lua_getglobal(thread.state, name); 614 | lookIntoStackTop(thread.state, std::forward(elements)...); 615 | return readTopAndPop(thread.state, PushedObject{thread.state, 1}); 616 | } 617 | 618 | /** 619 | * Changes the content of a Lua variable 620 | * 621 | * Accepted values are: 622 | * - all base types (char, short, int, float, double, bool) 623 | * - std::string 624 | * - enums 625 | * - std::vector<> 626 | * - std::vector>, std::map<> and std::unordered_map<> (the key and value must also be accepted values) 627 | * - std::function<> (all parameters must be accepted values, and return type must be either an accepted value for readVariable or a tuple) 628 | * - std::shared_ptr<> (std::unique_ptr<> are converted to std::shared_ptr<>) 629 | * - nullptr (writes nil) 630 | * - any object 631 | * 632 | * All objects are passed by copy and destroyed by the garbage collector if necessary. 633 | */ 634 | template 635 | void writeVariable(TData&&... data) noexcept { 636 | static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeVariable"); 637 | typedef typename std::decay>::type>::type 638 | RealDataType; 639 | static_assert(!std::is_same::type,RealDataType>::value, "Error: you can't use LuaContext::writeVariable with a tuple"); 640 | 641 | setTable(mState, Globals, std::forward(data)...); 642 | } 643 | 644 | /** 645 | * Equivalent to writeVariable(varName, ..., std::function(data)); 646 | * This version is more effecient than writeVariable if you want to write functions 647 | */ 648 | template 649 | void writeFunction(TData&&... data) noexcept { 650 | static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction"); 651 | 652 | setTable(mState, Globals, std::forward(data)...); 653 | } 654 | 655 | /** 656 | * Same as the other writeFunction, except that the template parameter is automatically detected 657 | * This only works if the data is either a native function pointer, or contains one operator() (this is the case for lambdas) 658 | */ 659 | template 660 | void writeFunction(TData&&... data) noexcept { 661 | static_assert(sizeof...(TData) >= 2, "You must pass at least a variable name and a value to writeFunction"); 662 | typedef typename std::decay>::type>::type 663 | RealDataType; 664 | typedef typename FunctionTypeDetector::type 665 | DetectedFunctionType; 666 | 667 | return writeFunction(std::forward(data)...); 668 | } 669 | 670 | 671 | private: 672 | // the state is the most important variable in the class since it is our interface with Lua 673 | // - registered members and functions are stored in tables at offset &typeid(type) of the registry 674 | // each table has its getter functions at offset 0, getter members at offset 1, default getter at offset 2 675 | // offset 3 is unused, setter members at offset 4, default setter at offset 5 676 | lua_State* mState; 677 | 678 | 679 | /**************************************************/ 680 | /* PUSH OBJECT */ 681 | /**************************************************/ 682 | struct PushedObject { 683 | PushedObject(lua_State* state, int num = 1) : state(state), num(num) {} 684 | ~PushedObject() { assert(lua_gettop(state) >= num); if (num >= 1) lua_pop(state, num); } 685 | 686 | PushedObject& operator=(const PushedObject&) = delete; 687 | PushedObject(const PushedObject&) = delete; 688 | PushedObject& operator=(PushedObject&& other) { std::swap(state, other.state); std::swap(num, other.num); return *this; } 689 | PushedObject(PushedObject&& other) : state(other.state), num(other.num) { other.num = 0; } 690 | 691 | PushedObject operator+(PushedObject&& other) && { PushedObject obj(state, num + other.num); num = 0; other.num = 0; return std::move(obj); } 692 | void operator+=(PushedObject&& other) { assert(state == other.state); num += other.num; other.num = 0; } 693 | 694 | auto getState() const -> lua_State* { return state; } 695 | auto getNum() const -> int { return num; } 696 | 697 | int release() { const auto n = num; num = 0; return n; } 698 | void pop() { if (num >= 1) lua_pop(state, num); num = 0; } 699 | void pop(int n) { assert(num >= n); lua_pop(state, n); num -= n; } 700 | 701 | private: 702 | lua_State* state; 703 | int num = 0; 704 | }; 705 | 706 | 707 | /**************************************************/ 708 | /* MISC */ 709 | /**************************************************/ 710 | // type used as a tag 711 | template 712 | struct tag {}; 713 | 714 | // tag for "the registry" 715 | enum RegistryTag { Registry }; 716 | 717 | // this function takes a value representing the offset to look into 718 | // it will look into the top element of the stack and replace the element by its content at the given index 719 | template 720 | static void lookIntoStackTop(lua_State* state, OffsetType1&& offset1, OffsetTypeOthers&&... offsetOthers) { 721 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 722 | auto p1 = Pusher::type>::push(state, offset1); 723 | lua_gettable(state, -2); 724 | lua_remove(state, -2); 725 | p1.release(); 726 | 727 | lookIntoStackTop(state, std::forward(offsetOthers)...); 728 | } 729 | 730 | template 731 | static void lookIntoStackTop(lua_State* state, Metatable_t, OffsetTypeOthers&&... offsetOthers) { 732 | lua_getmetatable(state, -1); 733 | lua_remove(state, -2); 734 | 735 | lookIntoStackTop(state, std::forward(offsetOthers)...); 736 | } 737 | 738 | static void lookIntoStackTop(lua_State*) { 739 | } 740 | 741 | // equivalent of lua_settable with t[k]=n, where t is the value at the index in the template parameter, k is the second parameter, n is the last parameter, and n is pushed by the function in the first parameter 742 | // if there are more than 3 parameters, parameters 3 to n-1 are considered as sub-indices into the array 743 | // the dataPusher MUST push only one thing on the stack 744 | // TTableIndex must be either LUA_REGISTERYINDEX, LUA_GLOBALSINDEX, LUA_ENVINDEX, or the position of the element on the stack 745 | template 746 | static void setTable(lua_State* state, const PushedObject&, TIndex&& index, TData&& data) noexcept 747 | { 748 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 749 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 750 | 751 | auto p1 = Pusher::type>::push(state, index); 752 | auto p2 = Pusher::type>::push(state, std::forward(data)); 753 | 754 | lua_settable(state, -3); 755 | p1.release(); 756 | p2.release(); 757 | } 758 | 759 | template 760 | static void setTable(lua_State* state, const PushedObject&, const std::string& index, TData&& data) noexcept 761 | { 762 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 763 | 764 | auto p1 = Pusher::type>::push(state, std::forward(data)); 765 | lua_setfield(state, -2, index.c_str()); 766 | p1.release(); 767 | } 768 | 769 | template 770 | static void setTable(lua_State* state, const PushedObject&, const char* index, TData&& data) noexcept 771 | { 772 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 773 | 774 | auto p1 = Pusher::type>::push(state, std::forward(data)); 775 | lua_setfield(state, -2, index); 776 | p1.release(); 777 | } 778 | 779 | template 780 | static void setTable(lua_State* state, const PushedObject&, Metatable_t, TData&& data) noexcept 781 | { 782 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 783 | 784 | auto p1 = Pusher::type>::push(state, std::forward(data)); 785 | lua_setmetatable(state, -2); 786 | p1.release(); 787 | } 788 | 789 | template 790 | static auto setTable(lua_State* state, PushedObject&, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 791 | -> typename std::enable_if::type, Metatable_t>::value>::type 792 | { 793 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 794 | 795 | auto p1 = Pusher::type>::push(state, std::forward(index1)); 796 | lua_gettable(state, -2); 797 | 798 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 799 | } 800 | 801 | template 802 | static auto setTable(lua_State* state, PushedObject&& pushedTable, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 803 | -> typename std::enable_if::type, Metatable_t>::value>::type 804 | { 805 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 806 | 807 | auto p1 = Pusher::type>::push(state, std::forward(index1)) + std::move(pushedTable); 808 | lua_gettable(state, -2); 809 | 810 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 811 | } 812 | 813 | template 814 | static void setTable(lua_State* state, PushedObject& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 815 | { 816 | if (lua_getmetatable(state, -1) == 0) 817 | { 818 | lua_newtable(state); 819 | PushedObject p1{state, 1}; 820 | 821 | setTable(state, p1, std::forward(index2), std::forward(index3), std::forward(indices)...); 822 | 823 | lua_setmetatable(state, -2); 824 | p1.release(); 825 | } 826 | else 827 | { 828 | setTable(state, pushedObject, std::forward(index2), std::forward(index3), std::forward(indices)...); 829 | } 830 | } 831 | 832 | template 833 | static void setTable(lua_State* state, PushedObject&& pushedObject, Metatable_t, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 834 | { 835 | if (lua_getmetatable(state, -1) == 0) 836 | { 837 | lua_newtable(state); 838 | PushedObject p1{state, 1}; 839 | 840 | setTable(state, p1, std::forward(index2), std::forward(index3), std::forward(indices)...); 841 | 842 | lua_setmetatable(state, -2); 843 | p1.release(); 844 | } 845 | else 846 | { 847 | setTable(state, std::move(pushedObject), std::forward(index2), std::forward(index3), std::forward(indices)...); 848 | } 849 | } 850 | 851 | template 852 | static void setTable(lua_State* state, RegistryTag, TIndex&& index, TData&& data) noexcept 853 | { 854 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 855 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 856 | 857 | auto p1 = Pusher::type>::push(state, index); 858 | auto p2 = Pusher::type>::push(state, std::forward(data)); 859 | 860 | lua_settable(state, LUA_REGISTRYINDEX); 861 | p1.release(); 862 | p2.release(); 863 | } 864 | 865 | template 866 | static void setTable(lua_State* state, RegistryTag, const std::string& index, TData&& data) noexcept 867 | { 868 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 869 | 870 | auto p1 = Pusher::type>::push(state, std::forward(data)); 871 | lua_setfield(state, LUA_REGISTRYINDEX, index.c_str()); 872 | p1.release(); 873 | } 874 | 875 | template 876 | static void setTable(lua_State* state, RegistryTag, const char* index, TData&& data) noexcept 877 | { 878 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 879 | 880 | auto p1 = Pusher::type>::push(state, std::forward(data)); 881 | lua_setfield(state, LUA_REGISTRYINDEX, index); 882 | p1.release(); 883 | } 884 | 885 | template 886 | static void setTable(lua_State* state, RegistryTag, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 887 | { 888 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 889 | 890 | auto p1 = Pusher::type>::push(state, std::forward(index1)); 891 | lua_gettable(state, LUA_REGISTRYINDEX); 892 | 893 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 894 | } 895 | 896 | template 897 | static void setTable(lua_State* state, Globals_t, TIndex&& index, TData&& data) noexcept 898 | { 899 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 900 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 901 | 902 | 903 | # if LUA_VERSION_NUM >= 502 904 | 905 | lua_pushglobaltable(state); 906 | PushedObject p3{state, 1}; 907 | auto p1 = Pusher::type>::push(state, index); 908 | auto p2 = Pusher::type>::push(state, std::forward(data)); 909 | lua_settable(state, -3); 910 | 911 | # else 912 | 913 | auto p1 = Pusher::type>::push(state, index); 914 | auto p2 = Pusher::type>::push(state, std::forward(data)); 915 | lua_settable(state, LUA_GLOBALSINDEX); 916 | 917 | # endif 918 | 919 | p1.release(); 920 | p2.release(); 921 | } 922 | 923 | template 924 | static void setTable(lua_State* state, Globals_t, const std::string& index, TData&& data) noexcept 925 | { 926 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 927 | 928 | auto p1 = Pusher::type>::push(state, std::forward(data)); 929 | lua_setglobal(state, index.c_str()); 930 | p1.release(); 931 | } 932 | 933 | template 934 | static void setTable(lua_State* state, Globals_t, const char* index, TData&& data) noexcept 935 | { 936 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values data"); 937 | 938 | auto p1 = Pusher::type>::push(state, std::forward(data)); 939 | lua_setglobal(state, index); 940 | p1.release(); 941 | } 942 | 943 | template 944 | static void setTable(lua_State* state, Globals_t, TIndex1&& index1, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 945 | { 946 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Impossible to have a multiple-values index"); 947 | 948 | # if LUA_VERSION_NUM >= 502 949 | 950 | lua_pushglobaltable(state); 951 | auto p1 = Pusher::type>::push(state, std::forward(index1)) + PushedObject{state, 1}; 952 | lua_gettable(state, -2); 953 | 954 | # else 955 | 956 | auto p1 = Pusher::type>::push(state, std::forward(index1)); 957 | lua_gettable(state, LUA_GLOBALSINDEX); 958 | 959 | # endif 960 | 961 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 962 | } 963 | 964 | // TODO: g++ reports "ambiguous overload" 965 | /*template 966 | static void setTable(lua_State* state, Globals_t, const char* index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 967 | { 968 | lua_getglobal(state, index); 969 | PushedObject p1{state, 1}; 970 | 971 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 972 | } 973 | 974 | template 975 | static void setTable(lua_State* state, Globals_t, const std::string& index, TIndex2&& index2, TIndex3&& index3, TIndices&&... indices) noexcept 976 | { 977 | lua_getglobal(state, index.c_str()); 978 | PushedObject p1{state, 1}; 979 | 980 | setTable(state, std::move(p1), std::forward(index2), std::forward(index3), std::forward(indices)...); 981 | }*/ 982 | 983 | // simple function that reads the "nb" first top elements of the stack, pops them, and returns the value 984 | // warning: first parameter is the number of parameters, not the parameter index 985 | // if read generates an exception, stack is poped anyway 986 | template 987 | static auto readTopAndPop(lua_State* state, PushedObject object) 988 | -> TReturnType 989 | { 990 | auto val = Reader::type>::read(state, -object.getNum()); 991 | if (!val.is_initialized()) 992 | throw WrongTypeException{lua_typename(state, lua_type(state, -object.getNum())), typeid(TReturnType)}; 993 | return val.get(); 994 | } 995 | 996 | // checks that the offsets for a type's registrations are set in the registry 997 | static void checkTypeRegistration(lua_State* state, const std::type_info* type) 998 | { 999 | lua_pushlightuserdata(state, const_cast(type)); 1000 | lua_gettable(state, LUA_REGISTRYINDEX); 1001 | if (!lua_isnil(state, -1)) { 1002 | lua_pop(state, 1); 1003 | return; 1004 | } 1005 | lua_pop(state, 1); 1006 | 1007 | lua_pushlightuserdata(state, const_cast(type)); 1008 | lua_newtable(state); 1009 | 1010 | lua_pushinteger(state, 0); 1011 | lua_newtable(state); 1012 | lua_settable(state, -3); 1013 | 1014 | lua_pushinteger(state, 1); 1015 | lua_newtable(state); 1016 | lua_settable(state, -3); 1017 | 1018 | lua_pushinteger(state, 3); 1019 | lua_newtable(state); 1020 | lua_settable(state, -3); 1021 | 1022 | lua_pushinteger(state, 4); 1023 | lua_newtable(state); 1024 | lua_settable(state, -3); 1025 | 1026 | lua_settable(state, LUA_REGISTRYINDEX); 1027 | } 1028 | 1029 | // 1030 | # ifdef _MSC_VER 1031 | __declspec(noreturn) 1032 | # else 1033 | [[noreturn]] 1034 | # endif 1035 | static void luaError(lua_State* state) 1036 | { 1037 | lua_error(state); 1038 | assert(false); 1039 | std::terminate(); // removes compilation warning 1040 | } 1041 | 1042 | 1043 | /**************************************************/ 1044 | /* FUNCTIONS REGISTRATION */ 1045 | /**************************************************/ 1046 | // the "registerFunction" public functions call this one 1047 | template 1048 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag, tag) 1049 | { 1050 | static_assert(std::is_class::value || std::is_pointer::value || std::is_union::value , "registerFunction can only be used for a class a union or a pointer"); 1051 | 1052 | checkTypeRegistration(mState, &typeid(TObject)); 1053 | setTable(mState, Registry, &typeid(TObject), 0, functionName, std::move(function)); 1054 | 1055 | checkTypeRegistration(mState, &typeid(TObject*)); 1056 | setTable(mState, Registry, &typeid(TObject*), 0, functionName, [=](TObject* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); 1057 | 1058 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1059 | setTable, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr), 0, functionName, [=](const std::shared_ptr& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); 1060 | } 1061 | 1062 | template 1063 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag, tag fTypeTag) 1064 | { 1065 | registerFunctionImpl(functionName, function, tag{}, fTypeTag); 1066 | 1067 | checkTypeRegistration(mState, &typeid(TObject const*)); 1068 | setTable(mState, Registry, &typeid(TObject const*), 0, functionName, [=](TObject const* obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); 1069 | 1070 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1071 | setTable, TOtherParams...)>(mState, Registry, &typeid(std::shared_ptr), 0, functionName, [=](const std::shared_ptr& obj, TOtherParams... rest) { assert(obj); return function(*obj, std::forward(rest)...); }); 1072 | } 1073 | 1074 | template 1075 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) 1076 | { 1077 | registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); 1078 | } 1079 | 1080 | template 1081 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) 1082 | { 1083 | registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); 1084 | } 1085 | 1086 | template 1087 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) 1088 | { 1089 | registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); 1090 | } 1091 | 1092 | template 1093 | void registerFunctionImpl(const std::string& functionName, TFunctionType function, tag) 1094 | { 1095 | registerFunctionImpl(functionName, std::move(function), tag{}, tag{}); 1096 | } 1097 | 1098 | // the "registerMember" public functions call this one 1099 | template 1100 | void registerMemberImpl(const std::string& name, TReadFunction readFunction) 1101 | { 1102 | static_assert(std::is_class::value || std::is_pointer::value, "registerMember can only be called on a class or a pointer"); 1103 | 1104 | checkTypeRegistration(mState, &typeid(TObject)); 1105 | setTable(mState, Registry, &typeid(TObject), 1, name, [readFunction](TObject const& object) { 1106 | return readFunction(object); 1107 | }); 1108 | 1109 | checkTypeRegistration(mState, &typeid(TObject*)); 1110 | setTable(mState, Registry, &typeid(TObject*), 1, name, [readFunction](TObject const* object) { 1111 | assert(object); 1112 | return readFunction(*object); 1113 | }); 1114 | 1115 | checkTypeRegistration(mState, &typeid(TObject const*)); 1116 | setTable(mState, Registry, &typeid(TObject const*), 1, name, [readFunction](TObject const* object) { 1117 | assert(object); 1118 | return readFunction(*object); 1119 | }); 1120 | 1121 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1122 | setTable)>(mState, Registry, &typeid(std::shared_ptr), 1, name, [readFunction](const std::shared_ptr& object) { 1123 | assert(object); 1124 | return readFunction(*object); 1125 | }); 1126 | 1127 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1128 | setTable)>(mState, Registry, &typeid(std::shared_ptr), 1, name, [readFunction](const std::shared_ptr& object) { 1129 | assert(object); 1130 | return readFunction(*object); 1131 | }); 1132 | } 1133 | 1134 | template 1135 | void registerMemberImpl(const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction) 1136 | { 1137 | registerMemberImpl(name, readFunction); 1138 | 1139 | setTable(mState, Registry, &typeid(TObject), 4, name, [writeFunction](TObject& object, const TVarType& value) { 1140 | writeFunction(object, value); 1141 | }); 1142 | 1143 | setTable(mState, Registry, &typeid(TObject*), 4, name, [writeFunction](TObject* object, const TVarType& value) { 1144 | assert(object); 1145 | writeFunction(*object, value); 1146 | }); 1147 | 1148 | setTable, TVarType)>(mState, Registry, &typeid(std::shared_ptr), 4, name, [writeFunction](std::shared_ptr object, const TVarType& value) { 1149 | assert(object); 1150 | writeFunction(*object, value); 1151 | }); 1152 | } 1153 | 1154 | template 1155 | void registerMemberImpl(tag, const std::string& name, TReadFunction readFunction, TWriteFunction writeFunction) 1156 | { 1157 | registerMemberImpl(name, std::move(readFunction), std::move(writeFunction)); 1158 | } 1159 | 1160 | template 1161 | void registerMemberImpl(tag, const std::string& name, TReadFunction readFunction) 1162 | { 1163 | registerMemberImpl(name, std::move(readFunction)); 1164 | } 1165 | 1166 | // the "registerMember" public functions call this one 1167 | template 1168 | void registerMemberImpl(TReadFunction readFunction) 1169 | { 1170 | checkTypeRegistration(mState, &typeid(TObject)); 1171 | setTable(mState, Registry, &typeid(TObject), 2, [readFunction](TObject const& object, const std::string& name) { 1172 | return readFunction(object, name); 1173 | }); 1174 | 1175 | checkTypeRegistration(mState, &typeid(TObject*)); 1176 | setTable(mState, Registry, &typeid(TObject*), 2, [readFunction](TObject const* object, const std::string& name) { 1177 | assert(object); 1178 | return readFunction(*object, name); 1179 | }); 1180 | 1181 | checkTypeRegistration(mState, &typeid(TObject const*)); 1182 | setTable(mState, Registry, &typeid(TObject const*), 2, [readFunction](TObject const* object, const std::string& name) { 1183 | assert(object); 1184 | return readFunction(*object, name); 1185 | }); 1186 | 1187 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1188 | setTable, std::string)>(mState, Registry, &typeid(std::shared_ptr), 2, [readFunction](const std::shared_ptr& object, const std::string& name) { 1189 | assert(object); 1190 | return readFunction(*object, name); 1191 | }); 1192 | 1193 | checkTypeRegistration(mState, &typeid(std::shared_ptr)); 1194 | setTable, std::string)>(mState, Registry, &typeid(std::shared_ptr), 2, [readFunction](const std::shared_ptr& object, const std::string& name) { 1195 | assert(object); 1196 | return readFunction(*object, name); 1197 | }); 1198 | } 1199 | 1200 | template 1201 | void registerMemberImpl(TReadFunction readFunction, TWriteFunction writeFunction) 1202 | { 1203 | registerMemberImpl(readFunction); 1204 | 1205 | setTable(mState, Registry, &typeid(TObject), 5, [writeFunction](TObject& object, const std::string& name, const TVarType& value) { 1206 | writeFunction(object, name, value); 1207 | }); 1208 | 1209 | setTable(mState, Registry, &typeid(TObject*), 2, [writeFunction](TObject* object, const std::string& name, const TVarType& value) { 1210 | assert(object); 1211 | writeFunction(*object, name, value); 1212 | }); 1213 | 1214 | setTable, std::string, TVarType)>(mState, Registry, &typeid(std::shared_ptr), 2, [writeFunction](const std::shared_ptr& object, const std::string& name, const TVarType& value) { 1215 | assert(object); 1216 | writeFunction(*object, name, value); 1217 | }); 1218 | } 1219 | 1220 | template 1221 | void registerMemberImpl(tag, TReadFunction readFunction, TWriteFunction writeFunction) 1222 | { 1223 | registerMemberImpl(std::move(readFunction), std::move(writeFunction)); 1224 | } 1225 | 1226 | template 1227 | void registerMemberImpl(tag, TReadFunction readFunction) 1228 | { 1229 | registerMemberImpl(std::move(readFunction)); 1230 | } 1231 | 1232 | 1233 | /**************************************************/ 1234 | /* LOADING AND CALLING */ 1235 | /**************************************************/ 1236 | // this function loads data from the stream and pushes a function at the top of the stack 1237 | // throws in case of syntax error 1238 | static PushedObject load(lua_State* state, std::istream& code) { 1239 | // since the lua_load function requires a static function, we use this structure 1240 | // the Reader structure is at the same time an object storing an istream and a buffer, 1241 | // and a static function provider 1242 | struct Reader { 1243 | Reader(std::istream& str) : stream(str) {} 1244 | std::istream& stream; 1245 | std::array buffer; 1246 | 1247 | // read function ; "data" must be an instance of Reader 1248 | static const char* read(lua_State* l, void* data, size_t* size) { 1249 | assert(size != nullptr); 1250 | assert(data != nullptr); 1251 | Reader& me = *static_cast(data); 1252 | if (me.stream.eof()) { *size = 0; return nullptr; } 1253 | 1254 | me.stream.read(me.buffer.data(), me.buffer.size()); 1255 | *size = static_cast(me.stream.gcount()); // gcount could return a value larger than a size_t, but its maximum is me.buffer.size() so there's no problem 1256 | return me.buffer.data(); 1257 | } 1258 | }; 1259 | 1260 | // we create an instance of Reader, and we call lua_load 1261 | Reader reader{code}; 1262 | const auto loadReturnValue = lua_load(state, &Reader::read, &reader, "chunk" 1263 | # if LUA_VERSION_NUM >= 502 1264 | , nullptr 1265 | # endif 1266 | ); 1267 | 1268 | // now we have to check return value 1269 | if (loadReturnValue != 0) { 1270 | // there was an error during loading, an error message was pushed on the stack 1271 | const std::string errorMsg = lua_tostring(state, -1); 1272 | lua_pop(state, 1); 1273 | if (loadReturnValue == LUA_ERRMEM) 1274 | throw std::bad_alloc(); 1275 | else if (loadReturnValue == LUA_ERRSYNTAX) 1276 | throw SyntaxErrorException{errorMsg}; 1277 | throw std::runtime_error("Error while calling lua_load: " + errorMsg); 1278 | } 1279 | 1280 | return PushedObject{state, 1}; 1281 | } 1282 | 1283 | // this function loads data and pushes a function at the top of the stack 1284 | // throws in case of syntax error 1285 | static PushedObject load(lua_State* state, const char* code) { 1286 | auto loadReturnValue = luaL_loadstring(state, code); 1287 | 1288 | // now we have to check return value 1289 | if (loadReturnValue != 0) { 1290 | // there was an error during loading, an error message was pushed on the stack 1291 | const std::string errorMsg = lua_tostring(state, -1); 1292 | lua_pop(state, 1); 1293 | if (loadReturnValue == LUA_ERRMEM) 1294 | throw std::bad_alloc(); 1295 | else if (loadReturnValue == LUA_ERRSYNTAX) 1296 | throw SyntaxErrorException{errorMsg}; 1297 | throw std::runtime_error("Error while calling lua_load: " + errorMsg); 1298 | } 1299 | 1300 | return PushedObject{state, 1}; 1301 | } 1302 | 1303 | // this function calls what is on the top of the stack and removes it (just like lua_call) 1304 | // if an exception is triggered, the top of the stack will be removed anyway 1305 | template 1306 | static auto call(lua_State* state, PushedObject toCall, TParameters&&... input) 1307 | -> TReturnType 1308 | { 1309 | typedef typename Tupleizer::type 1310 | RealReturnType; 1311 | 1312 | // we push the parameters on the stack 1313 | auto inArguments = Pusher>::push(state, std::make_tuple(std::forward(input)...)); 1314 | 1315 | // 1316 | const int outArgumentsCount = std::tuple_size::value; 1317 | auto outArguments = callRaw(state, std::move(toCall) + std::move(inArguments), outArgumentsCount); 1318 | 1319 | // pcall succeeded, we pop the returned values and return them 1320 | return readTopAndPop(state, std::move(outArguments)); 1321 | } 1322 | 1323 | // this function just calls lua_pcall and checks for errors 1324 | static PushedObject callRaw(lua_State* state, PushedObject functionAndArguments, const int outArguments) 1325 | { 1326 | // calling pcall automatically pops the parameters and pushes output 1327 | const auto pcallReturnValue = lua_pcall(state, functionAndArguments.getNum() - 1, outArguments, 0); 1328 | functionAndArguments.release(); 1329 | 1330 | // if pcall failed, analyzing the problem and throwing 1331 | if (pcallReturnValue != 0) { 1332 | PushedObject errorCode{state, 1}; 1333 | 1334 | // an error occured during execution, either an error message or a std::exception_ptr was pushed on the stack 1335 | if (pcallReturnValue == LUA_ERRMEM) { 1336 | throw std::bad_alloc{}; 1337 | 1338 | } else if (pcallReturnValue == LUA_ERRRUN) { 1339 | if (lua_isstring(state, 1)) { 1340 | // the error is a string 1341 | const auto str = readTopAndPop(state, std::move(errorCode)); 1342 | throw ExecutionErrorException{str}; 1343 | 1344 | } else { 1345 | // an exception_ptr was pushed on the stack 1346 | // rethrowing it with an additional ExecutionErrorException 1347 | try { 1348 | std::rethrow_exception(readTopAndPop(state, std::move(errorCode))); 1349 | } catch(...) { 1350 | std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua"}); 1351 | } 1352 | } 1353 | } 1354 | } 1355 | 1356 | return PushedObject{state, outArguments}; 1357 | } 1358 | 1359 | 1360 | /**************************************************/ 1361 | /* PUSH FUNCTIONS */ 1362 | /**************************************************/ 1363 | template 1364 | static PushedObject push(lua_State* state, T&& value) 1365 | { 1366 | return Pusher::type>::push(state, std::forward(value)); 1367 | } 1368 | 1369 | // the Pusher structures allow you to push a value on the stack 1370 | // - static const int minSize : minimum size on the stack that the value can have 1371 | // - static const int maxSize : maximum size on the stack that the value can have 1372 | // - static int push(const LuaContext&, ValueType) : pushes the value on the stack and returns the size on the stack 1373 | 1374 | // implementation for custom objects 1375 | template 1376 | struct Pusher { 1377 | static const int minSize = 1; 1378 | static const int maxSize = 1; 1379 | 1380 | template 1381 | static PushedObject push(lua_State* state, TType2&& value) noexcept { 1382 | // this function is called when lua's garbage collector wants to destroy our object 1383 | // we simply call its destructor 1384 | const auto garbageCallbackFunction = [](lua_State* lua) -> int { 1385 | assert(lua_gettop(lua) == 1); 1386 | TType* ptr = static_cast(lua_touserdata(lua, 1)); 1387 | assert(ptr); 1388 | ptr->~TType(); 1389 | return 0; 1390 | }; 1391 | 1392 | // this function will be stored in __index in the metatable 1393 | const auto indexFunction = [](lua_State* lua) -> int { 1394 | try { 1395 | assert(lua_gettop(lua) == 2); 1396 | assert(lua_isuserdata(lua, 1)); 1397 | 1398 | // searching for a handler 1399 | lua_pushlightuserdata(lua, const_cast(&typeid(TType))); 1400 | lua_gettable(lua, LUA_REGISTRYINDEX); 1401 | assert(!lua_isnil(lua, -1)); 1402 | 1403 | // looking into getter functions 1404 | lua_pushinteger(lua, 0); 1405 | lua_gettable(lua, -2); 1406 | lua_pushvalue(lua, 2); 1407 | lua_gettable(lua, -2); 1408 | if (!lua_isnil(lua, -1)) 1409 | return 1; 1410 | lua_pop(lua, 2); 1411 | 1412 | // looking into getter members 1413 | lua_pushinteger(lua, 1); 1414 | lua_gettable(lua, -2); 1415 | lua_pushvalue(lua, 2); 1416 | lua_gettable(lua, -2); 1417 | if (!lua_isnil(lua, -1)) { 1418 | lua_pushvalue(lua, 1); 1419 | return callRaw(lua, PushedObject{lua, 2}, 1).release(); 1420 | } 1421 | lua_pop(lua, 2); 1422 | 1423 | // looking into default getter 1424 | lua_pushinteger(lua, 2); 1425 | lua_gettable(lua, -2); 1426 | if (lua_isnil(lua, -1)) 1427 | return 1; 1428 | lua_pushvalue(lua, 1); 1429 | lua_pushvalue(lua, 2); 1430 | return callRaw(lua, PushedObject{lua, 3}, 1).release(); 1431 | 1432 | } catch (...) { 1433 | Pusher::push(lua, std::current_exception()).release(); 1434 | luaError(lua); 1435 | } 1436 | }; 1437 | 1438 | // this function will be stored in __newindex in the metatable 1439 | const auto newIndexFunction = [](lua_State* lua) -> int { 1440 | try { 1441 | assert(lua_gettop(lua) == 3); 1442 | assert(lua_isuserdata(lua, 1)); 1443 | 1444 | // searching for a handler 1445 | lua_pushlightuserdata(lua, const_cast(&typeid(TType))); 1446 | lua_rawget(lua, LUA_REGISTRYINDEX); 1447 | assert(!lua_isnil(lua, -1)); 1448 | 1449 | // looking into setter members 1450 | lua_pushinteger(lua, 4); 1451 | lua_rawget(lua, -2); 1452 | lua_pushvalue(lua, 2); 1453 | lua_rawget(lua, -2); 1454 | if (!lua_isnil(lua, -1)) { 1455 | lua_pushvalue(lua, 1); 1456 | lua_pushvalue(lua, 3); 1457 | callRaw(lua, PushedObject{lua, 3}, 0); 1458 | lua_pop(lua, 2); 1459 | return 0; 1460 | } 1461 | lua_pop(lua, 2); 1462 | 1463 | // looking into default setter 1464 | lua_pushinteger(lua, 5); 1465 | lua_rawget(lua, -2); 1466 | if (lua_isnil(lua, -1)) 1467 | { 1468 | lua_pop(lua, 2); 1469 | lua_pushstring(lua, "No setter found"); 1470 | luaError(lua); 1471 | } 1472 | lua_pushvalue(lua, 1); 1473 | lua_pushvalue(lua, 2); 1474 | lua_pushvalue(lua, 3); 1475 | callRaw(lua, PushedObject{lua, 4}, 0); 1476 | lua_pop(lua, 1); 1477 | return 0; 1478 | 1479 | } catch (...) { 1480 | Pusher::push(lua, std::current_exception()).release(); 1481 | luaError(lua); 1482 | } 1483 | }; 1484 | 1485 | // writing structure for this type into the registry 1486 | checkTypeRegistration(state, &typeid(TType)); 1487 | 1488 | // creating the object 1489 | // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it 1490 | // and that's what we do with placement-new 1491 | const auto pointerLocation = static_cast(lua_newuserdata(state, sizeof(TType))); 1492 | new (pointerLocation) TType(std::forward(value)); 1493 | PushedObject obj{state, 1}; 1494 | 1495 | // creating the metatable (over the object on the stack) 1496 | // lua_settable pops the key and value we just pushed, so stack management is easy 1497 | // all that remains on the stack after these function calls is the metatable 1498 | lua_newtable(state); 1499 | PushedObject pushedTable{state, 1}; 1500 | 1501 | // using the garbage collecting function we created above 1502 | if (!boost::has_trivial_destructor::value) 1503 | { 1504 | lua_pushstring(state, "__gc"); 1505 | lua_pushcfunction(state, garbageCallbackFunction); 1506 | lua_settable(state, -3); 1507 | } 1508 | 1509 | // the _typeid index of the metatable will store the type_info* 1510 | lua_pushstring(state, "_typeid"); 1511 | lua_pushlightuserdata(state, const_cast(&typeid(TType))); 1512 | lua_settable(state, -3); 1513 | 1514 | // using the index function we created above 1515 | lua_pushstring(state, "__index"); 1516 | lua_pushcfunction(state, indexFunction); 1517 | lua_settable(state, -3); 1518 | 1519 | // using the newindex function we created above 1520 | lua_pushstring(state, "__newindex"); 1521 | lua_pushcfunction(state, newIndexFunction); 1522 | lua_settable(state, -3); 1523 | 1524 | // at this point, the stack contains the object at offset -2 and the metatable at offset -1 1525 | // lua_setmetatable will bind the two together and pop the metatable 1526 | // our custom type remains on the stack (and that's what we want since this is a push function) 1527 | lua_setmetatable(state, -2); 1528 | pushedTable.release(); 1529 | 1530 | return std::move(obj); 1531 | } 1532 | }; 1533 | 1534 | // this structure has a "size" int static member which is equal to the total of the push min size of all the types 1535 | template 1536 | struct PusherTotalMinSize; 1537 | 1538 | // this structure has a "size" int static member which is equal to the total of the push max size of all the types 1539 | template 1540 | struct PusherTotalMaxSize; 1541 | 1542 | // this structure has a "size" int static member which is equal to the maximum size of the push of all the types 1543 | template 1544 | struct PusherMinSize; 1545 | 1546 | // this structure has a "size" int static member which is equal to the maximum size of the push of all the types 1547 | template 1548 | struct PusherMaxSize; 1549 | 1550 | 1551 | /**************************************************/ 1552 | /* READ FUNCTIONS */ 1553 | /**************************************************/ 1554 | // the "Reader" structures allow to read data from the stack 1555 | // - the "ReturnType" type is what is returned by the reader, and can be different than the template parameter (especially with references and constness) 1556 | // - the "read" static function will check and read at the same time, returning an empty optional if it is the wrong type 1557 | 1558 | template 1559 | struct Reader { 1560 | typedef typename std::conditional::value, TType, TType&>::type 1561 | ReturnType; 1562 | 1563 | static auto read(lua_State* state, int index) 1564 | -> boost::optional 1565 | { 1566 | if (!test(state, index)) 1567 | return boost::none; 1568 | return boost::optional(*static_cast(lua_touserdata(state, index))); 1569 | } 1570 | 1571 | private: 1572 | static bool test(lua_State* state, int index) 1573 | { 1574 | if (!lua_isuserdata(state, index)) 1575 | return false; 1576 | if (!lua_getmetatable(state, index)) 1577 | return false; 1578 | 1579 | // now we have our metatable on the top of the stack 1580 | // retrieving its _typeid member 1581 | lua_pushstring(state, "_typeid"); 1582 | lua_gettable(state, -2); 1583 | const auto storedTypeID = static_cast(lua_touserdata(state, -1)); 1584 | const auto typeIDToCompare = &typeid(TType); 1585 | 1586 | // if wrong typeid, returning false 1587 | lua_pop(state, 2); 1588 | if (storedTypeID != typeIDToCompare) 1589 | return false; 1590 | 1591 | return true; 1592 | } 1593 | }; 1594 | 1595 | /** 1596 | * This functions reads multiple values starting at "index" and passes them to the callback 1597 | */ 1598 | template 1599 | static auto readIntoFunction(lua_State* state, tag, TCallback&& callback, int index) 1600 | -> TRetValue 1601 | { 1602 | return callback(); 1603 | } 1604 | template 1605 | static auto readIntoFunction(lua_State* state, tag retValueTag, TCallback&& callback, int index, tag, tag... othersTags) 1606 | -> typename std::enable_if::value, TRetValue>::type 1607 | { 1608 | if (index >= 0) { 1609 | Binder binder{ callback, {} }; 1610 | return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); 1611 | } 1612 | 1613 | const auto& firstElem = Reader::type>::read(state, index); 1614 | if (!firstElem) 1615 | throw WrongTypeException(lua_typename(state, index), typeid(TFirstType)); 1616 | 1617 | Binder binder{ callback, *firstElem }; 1618 | return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); 1619 | } 1620 | template 1621 | static auto readIntoFunction(lua_State* state, tag retValueTag, TCallback&& callback, int index, tag, tag... othersTags) 1622 | -> typename std::enable_if::value, TRetValue>::type 1623 | { 1624 | if (index >= 0) 1625 | throw std::logic_error("Wrong number of parameters"); 1626 | 1627 | const auto& firstElem = Reader::type>::read(state, index); 1628 | if (!firstElem) 1629 | throw WrongTypeException(lua_typename(state, index), typeid(TFirstType)); 1630 | 1631 | Binder binder{ callback, *firstElem }; 1632 | return readIntoFunction(state, retValueTag, binder, index + 1, othersTags...); 1633 | } 1634 | 1635 | 1636 | /**************************************************/ 1637 | /* UTILITIES */ 1638 | /**************************************************/ 1639 | // structure that will ensure that a certain is stored somewhere in the registry 1640 | struct ValueInRegistry { 1641 | // this constructor will clone and hold the value at the top of the stack in the registry 1642 | ValueInRegistry(lua_State* lua) : lua{lua} 1643 | { 1644 | lua_pushlightuserdata(lua, this); 1645 | lua_pushvalue(lua, -2); 1646 | lua_settable(lua, LUA_REGISTRYINDEX); 1647 | } 1648 | 1649 | // removing the function from the registry 1650 | ~ValueInRegistry() 1651 | { 1652 | lua_pushlightuserdata(lua, this); 1653 | lua_pushnil(lua); 1654 | lua_settable(lua, LUA_REGISTRYINDEX); 1655 | } 1656 | 1657 | // loads the value and puts it at the top of the stack 1658 | PushedObject pop() 1659 | { 1660 | lua_pushlightuserdata(lua, this); 1661 | lua_gettable(lua, LUA_REGISTRYINDEX); 1662 | return PushedObject{lua, 1}; 1663 | } 1664 | 1665 | ValueInRegistry(const ValueInRegistry&) = delete; 1666 | ValueInRegistry& operator=(const ValueInRegistry&) = delete; 1667 | 1668 | private: 1669 | lua_State* lua; 1670 | }; 1671 | 1672 | // binds the first parameter of a function object 1673 | template 1674 | struct Binder { 1675 | TFunctionObject function; 1676 | TFirstParamType param; 1677 | 1678 | template 1679 | auto operator()(TParams&&... params) 1680 | -> decltype(function(param, std::forward(params)...)) 1681 | { 1682 | return function(param, std::forward(params)...); 1683 | } 1684 | }; 1685 | 1686 | // turns a type into a tuple 1687 | // void is turned into std::tuple<> 1688 | // existing tuples are untouched 1689 | template 1690 | struct Tupleizer; 1691 | 1692 | // this structure takes a pointer to a member function type and returns the base function type 1693 | template 1694 | struct RemoveMemberPointerFunction { typedef void type; }; // required because of a compiler bug 1695 | 1696 | // this structure takes any object and detects its function type 1697 | template 1698 | struct FunctionTypeDetector { typedef typename RemoveMemberPointerFunction::type::operator())>::type type; }; 1699 | 1700 | // this structure takes a function arguments list and has the "min" and the "max" static const member variables, whose value equal to the min and max number of parameters for the function 1701 | // the only case where "min != max" is with boost::optional at the end of the list 1702 | template 1703 | struct FunctionArgumentsCounter {}; 1704 | 1705 | // true is the template parameter is a boost::optional 1706 | template 1707 | struct IsOptional : public std::false_type {}; 1708 | }; 1709 | 1710 | /// @deprecated 1711 | static LuaContext::EmptyArray_t ATTR_UNUSED 1712 | LuaEmptyArray {}; 1713 | /// @deprecated 1714 | static LuaContext::Metatable_t ATTR_UNUSED 1715 | LuaMetatable {}; 1716 | 1717 | /**************************************************/ 1718 | /* PARTIAL IMPLEMENTATIONS */ 1719 | /**************************************************/ 1720 | template<> 1721 | inline auto LuaContext::readTopAndPop(lua_State* state, PushedObject obj) 1722 | -> void 1723 | { 1724 | } 1725 | 1726 | // this structure takes a template parameter T 1727 | // if T is a tuple, it returns T ; if T is not a tuple, it returns std::tuple 1728 | // we have to use this structure because std::tuple> triggers a bug in both MSVC++ and GCC 1729 | template 1730 | struct LuaContext::Tupleizer { typedef std::tuple type; }; 1731 | template 1732 | struct LuaContext::Tupleizer> { typedef std::tuple type; }; 1733 | template<> 1734 | struct LuaContext::Tupleizer { typedef std::tuple<> type; }; 1735 | 1736 | // this structure takes any object and detects its function type 1737 | template 1738 | struct LuaContext::FunctionTypeDetector { typedef TRetValue type(TParameters...); }; 1739 | template 1740 | struct LuaContext::FunctionTypeDetector { typedef typename FunctionTypeDetector::type type; }; 1741 | 1742 | // this structure takes a pointer to a member function type and returns the base function type 1743 | template 1744 | struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; 1745 | template 1746 | struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; 1747 | template 1748 | struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; 1749 | template 1750 | struct LuaContext::RemoveMemberPointerFunction { typedef TRetValue type(TParameters...); }; 1751 | 1752 | // implementation of PusherTotalMinSize 1753 | template 1754 | struct LuaContext::PusherTotalMinSize { static const int size = Pusher::type>::minSize + PusherTotalMinSize::size; }; 1755 | template<> 1756 | struct LuaContext::PusherTotalMinSize<> { static const int size = 0; }; 1757 | 1758 | // implementation of PusherTotalMaxSize 1759 | template 1760 | struct LuaContext::PusherTotalMaxSize { static const int size = Pusher::type>::maxSize + PusherTotalMaxSize::size; }; 1761 | template<> 1762 | struct LuaContext::PusherTotalMaxSize<> { static const int size = 0; }; 1763 | 1764 | // implementation of PusherMinSize 1765 | template 1766 | struct LuaContext::PusherMinSize { static const int size = Pusher::type>::minSize < PusherTotalMaxSize::size ? Pusher::type>::minSize : PusherMinSize::size; }; 1767 | template<> 1768 | struct LuaContext::PusherMinSize<> { static const int size = 0; }; 1769 | 1770 | // implementation of PusherMaxSize 1771 | template 1772 | struct LuaContext::PusherMaxSize { static const int size = Pusher::type>::maxSize > PusherTotalMaxSize::size ? Pusher::type>::maxSize : PusherMaxSize::size; }; 1773 | template<> 1774 | struct LuaContext::PusherMaxSize<> { static const int size = 0; }; 1775 | 1776 | // implementation of FunctionArgumentsCounter 1777 | template 1778 | struct LuaContext::FunctionArgumentsCounter { 1779 | typedef FunctionArgumentsCounter 1780 | SubType; 1781 | static const int min = (IsOptional::value && SubType::min == 0) ? 0 : 1 + SubType::min; 1782 | static const int max = 1 + SubType::max; 1783 | }; 1784 | template<> 1785 | struct LuaContext::FunctionArgumentsCounter<> { 1786 | static const int min = 0; 1787 | static const int max = 0; 1788 | }; 1789 | 1790 | // implementation of IsOptional 1791 | template 1792 | struct LuaContext::IsOptional> : public std::true_type {}; 1793 | 1794 | // implementation of LuaFunctionCaller 1795 | template 1796 | class LuaContext::LuaFunctionCaller { static_assert(std::is_function::value, "Template parameter of LuaFunctionCaller must be a function type"); }; 1797 | template 1798 | class LuaContext::LuaFunctionCaller 1799 | { 1800 | public: 1801 | TRetValue operator()(TParams&&... params) const 1802 | { 1803 | auto obj = valueHolder->pop(); 1804 | return call(state, std::move(obj), std::forward(params)...); 1805 | } 1806 | 1807 | private: 1808 | std::shared_ptr valueHolder; 1809 | lua_State* state; 1810 | 1811 | private: 1812 | friend LuaContext; 1813 | explicit LuaFunctionCaller(lua_State* state) : 1814 | valueHolder(std::make_shared(state)), 1815 | state(state) 1816 | {} 1817 | }; 1818 | 1819 | 1820 | /**************************************************/ 1821 | /* PUSH FUNCTIONS */ 1822 | /**************************************************/ 1823 | // specializations of the Pusher structure 1824 | 1825 | // boolean 1826 | template<> 1827 | struct LuaContext::Pusher { 1828 | static const int minSize = 1; 1829 | static const int maxSize = 1; 1830 | 1831 | static PushedObject push(lua_State* state, bool value) noexcept { 1832 | lua_pushboolean(state, value); 1833 | return PushedObject{state, 1}; 1834 | } 1835 | }; 1836 | 1837 | // string 1838 | template<> 1839 | struct LuaContext::Pusher { 1840 | static const int minSize = 1; 1841 | static const int maxSize = 1; 1842 | 1843 | static PushedObject push(lua_State* state, const std::string& value) noexcept { 1844 | lua_pushstring(state, value.c_str()); 1845 | return PushedObject{state, 1}; 1846 | } 1847 | }; 1848 | 1849 | // const char* 1850 | template<> 1851 | struct LuaContext::Pusher { 1852 | static const int minSize = 1; 1853 | static const int maxSize = 1; 1854 | 1855 | static PushedObject push(lua_State* state, const char* value) noexcept { 1856 | lua_pushstring(state, value); 1857 | return PushedObject{state, 1}; 1858 | } 1859 | }; 1860 | 1861 | // const char[N] 1862 | template 1863 | struct LuaContext::Pusher { 1864 | static const int minSize = 1; 1865 | static const int maxSize = 1; 1866 | 1867 | static PushedObject push(lua_State* state, const char* value) noexcept { 1868 | lua_pushstring(state, value); 1869 | return PushedObject{state, 1}; 1870 | } 1871 | }; 1872 | 1873 | // floating numbers 1874 | template 1875 | struct LuaContext::Pusher::value>::type> { 1876 | static const int minSize = 1; 1877 | static const int maxSize = 1; 1878 | 1879 | static PushedObject push(lua_State* state, T value) noexcept { 1880 | lua_pushnumber(state, value); 1881 | return PushedObject{state, 1}; 1882 | } 1883 | }; 1884 | 1885 | // integers 1886 | template 1887 | struct LuaContext::Pusher::value>::type> { 1888 | static const int minSize = 1; 1889 | static const int maxSize = 1; 1890 | 1891 | static PushedObject push(lua_State* state, T value) noexcept { 1892 | lua_pushinteger(state, value); 1893 | return PushedObject{state, 1}; 1894 | } 1895 | }; 1896 | 1897 | // nil 1898 | template<> 1899 | struct LuaContext::Pusher { 1900 | static const int minSize = 1; 1901 | static const int maxSize = 1; 1902 | 1903 | static PushedObject push(lua_State* state, std::nullptr_t value) noexcept { 1904 | assert(value == nullptr); 1905 | lua_pushnil(state); 1906 | return PushedObject{state, 1}; 1907 | } 1908 | }; 1909 | 1910 | // empty arrays 1911 | template<> 1912 | struct LuaContext::Pusher { 1913 | static const int minSize = 1; 1914 | static const int maxSize = 1; 1915 | 1916 | static PushedObject push(lua_State* state, EmptyArray_t) noexcept { 1917 | lua_newtable(state); 1918 | return PushedObject{state, 1}; 1919 | } 1920 | }; 1921 | 1922 | // std::type_info* is a lightuserdata 1923 | template<> 1924 | struct LuaContext::Pusher { 1925 | static const int minSize = 1; 1926 | static const int maxSize = 1; 1927 | 1928 | static PushedObject push(lua_State* state, const std::type_info* ptr) noexcept { 1929 | lua_pushlightuserdata(state, const_cast(ptr)); 1930 | return PushedObject{state, 1}; 1931 | } 1932 | }; 1933 | 1934 | // thread 1935 | template<> 1936 | struct LuaContext::Pusher { 1937 | static const int minSize = 1; 1938 | static const int maxSize = 1; 1939 | 1940 | static PushedObject push(lua_State* state, const LuaContext::ThreadID& value) noexcept { 1941 | lua_pushthread(value.state); 1942 | return PushedObject{state, 1}; 1943 | } 1944 | }; 1945 | 1946 | // maps 1947 | template 1948 | struct LuaContext::Pusher> { 1949 | static const int minSize = 1; 1950 | static const int maxSize = 1; 1951 | 1952 | static PushedObject push(lua_State* state, const std::map& value) noexcept { 1953 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); 1954 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); 1955 | 1956 | auto obj = Pusher::push(state, EmptyArray); 1957 | 1958 | for (auto i = value.begin(), e = value.end(); i != e; ++i) 1959 | setTable(state, obj, i->first, i->second); 1960 | 1961 | return std::move(obj); 1962 | } 1963 | }; 1964 | 1965 | // unordered_maps 1966 | template 1967 | struct LuaContext::Pusher> { 1968 | static const int minSize = 1; 1969 | static const int maxSize = 1; 1970 | 1971 | static PushedObject push(lua_State* state, const std::unordered_map& value) noexcept { 1972 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); 1973 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); 1974 | 1975 | auto obj = Pusher::push(state, EmptyArray); 1976 | 1977 | for (auto i = value.begin(), e = value.end(); i != e; ++i) 1978 | setTable(state, obj, i->first, i->second); 1979 | 1980 | return std::move(obj); 1981 | } 1982 | }; 1983 | 1984 | // vectors of pairs 1985 | template 1986 | struct LuaContext::Pusher>> { 1987 | static const int minSize = 1; 1988 | static const int maxSize = 1; 1989 | 1990 | static PushedObject push(lua_State* state, const std::vector>& value) noexcept { 1991 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table key"); 1992 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); 1993 | 1994 | auto obj = Pusher::push(state, EmptyArray); 1995 | 1996 | for (auto i = value.begin(), e = value.end(); i != e; ++i) 1997 | setTable(state, obj, i->first, i->second); 1998 | 1999 | return std::move(obj); 2000 | } 2001 | }; 2002 | 2003 | // vectors 2004 | template 2005 | struct LuaContext::Pusher> { 2006 | static const int minSize = 1; 2007 | static const int maxSize = 1; 2008 | 2009 | static PushedObject push(lua_State* state, const std::vector& value) noexcept { 2010 | static_assert(Pusher::type>::minSize == 1 && Pusher::type>::maxSize == 1, "Can't push multiple elements for a table value"); 2011 | 2012 | auto obj = Pusher::push(state, EmptyArray); 2013 | 2014 | for (unsigned int i = 0; i < value.size(); ++i) 2015 | setTable(state, obj, i + 1, value[i]); 2016 | 2017 | return std::move(obj); 2018 | } 2019 | }; 2020 | 2021 | // unique_ptr 2022 | template 2023 | struct LuaContext::Pusher> { 2024 | static const int minSize = Pusher>::minSize; 2025 | static const int maxSize = Pusher>::maxSize; 2026 | 2027 | static PushedObject push(lua_State* state, std::unique_ptr value) noexcept { 2028 | return Pusher>::push(state, std::move(value)); 2029 | } 2030 | }; 2031 | 2032 | // enum 2033 | template 2034 | struct LuaContext::Pusher::value>::type> { 2035 | #if !defined(__clang__) || __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3) 2036 | typedef typename std::underlying_type::type 2037 | RealType; 2038 | #else 2039 | // implementation when std::underlying_type is not supported 2040 | typedef unsigned long 2041 | RealType; 2042 | #endif 2043 | 2044 | static const int minSize = Pusher::minSize; 2045 | static const int maxSize = Pusher::maxSize; 2046 | 2047 | static PushedObject push(lua_State* state, TEnum value) noexcept { 2048 | return Pusher::push(state, static_cast(value)); 2049 | } 2050 | }; 2051 | 2052 | // any function 2053 | // this specialization is not directly called, but is called by other specializations 2054 | template 2055 | struct LuaContext::Pusher 2056 | { 2057 | static const int minSize = 1; 2058 | static const int maxSize = 1; 2059 | 2060 | // counts the number of arguments 2061 | typedef FunctionArgumentsCounter 2062 | LocalFunctionArgumentsCounter; 2063 | 2064 | // this is the version of "push" for non-trivially destructible function objects 2065 | template 2066 | static auto push(lua_State* state, TFunctionObject fn) noexcept 2067 | -> typename std::enable_if::value, PushedObject>::type 2068 | { 2069 | // TODO: is_move_constructible not supported by some compilers 2070 | //static_assert(std::is_move_constructible::value, "The function object must be move-constructible"); 2071 | 2072 | // when the lua script calls the thing we will push on the stack, we want "fn" to be executed 2073 | // if we used lua's cfunctions system, we could not detect when the function is no longer in use, which could cause problems 2074 | // so we use userdata instead 2075 | 2076 | // this function is called when the lua script tries to call our custom data type 2077 | // we transfer execution to the "callback" function below 2078 | const auto callCallback = [](lua_State* lua) -> int { 2079 | assert(lua_gettop(lua) >= 1); 2080 | assert(lua_isuserdata(lua, 1)); 2081 | auto function = static_cast(lua_touserdata(lua, 1)); 2082 | assert(function); 2083 | 2084 | return callback(lua, function, lua_gettop(lua) - 1).release(); 2085 | }; 2086 | 2087 | // this one is called when lua's garbage collector no longer needs our custom data type 2088 | // we call the function object's destructor 2089 | const auto garbageCallback = [](lua_State* lua) -> int { 2090 | assert(lua_gettop(lua) == 1); 2091 | auto function = static_cast(lua_touserdata(lua, 1)); 2092 | assert(function); 2093 | function->~TFunctionObject(); 2094 | return 0; 2095 | }; 2096 | 2097 | // creating the object 2098 | // lua_newuserdata allocates memory in the internals of the lua library and returns it so we can fill it 2099 | // and that's what we do with placement-new 2100 | const auto functionLocation = static_cast(lua_newuserdata(state, sizeof(TFunctionObject))); 2101 | new (functionLocation) TFunctionObject(std::move(fn)); 2102 | 2103 | // creating the metatable (over the object on the stack) 2104 | // lua_settable pops the key and value we just pushed, so stack management is easy 2105 | // all that remains on the stack after these function calls is the metatable 2106 | lua_newtable(state); 2107 | lua_pushstring(state, "__call"); 2108 | lua_pushcfunction(state, callCallback); 2109 | lua_settable(state, -3); 2110 | 2111 | lua_pushstring(state, "__gc"); 2112 | lua_pushcfunction(state, garbageCallback); 2113 | lua_settable(state, -3); 2114 | 2115 | // at this point, the stack contains the object at offset -2 and the metatable at offset -1 2116 | // lua_setmetatable will bind the two together and pop the metatable 2117 | // our custom function remains on the stack (and that's what we want) 2118 | lua_setmetatable(state, -2); 2119 | 2120 | return PushedObject{state, 1}; 2121 | } 2122 | 2123 | // this is the version of "push" for trivially destructible objects 2124 | template 2125 | static auto push(lua_State* state, TFunctionObject fn) noexcept 2126 | -> typename std::enable_if::value, PushedObject>::type 2127 | { 2128 | // TODO: is_move_constructible not supported by some compilers 2129 | //static_assert(std::is_move_constructible::value, "The function object must be move-constructible"); 2130 | 2131 | // when the lua script calls the thing we will push on the stack, we want "fn" to be executed 2132 | // since "fn" doesn't need to be destroyed, we simply push it on the stack 2133 | 2134 | // this is the cfunction that is the callback 2135 | const auto function = [](lua_State* state) -> int 2136 | { 2137 | // the function object is an upvalue 2138 | const auto toCall = static_cast(lua_touserdata(state, lua_upvalueindex(1))); 2139 | return callback(state, toCall, lua_gettop(state)).release(); 2140 | }; 2141 | 2142 | // we copy the function object onto the stack 2143 | const auto functionObjectLocation = static_cast(lua_newuserdata(state, sizeof(TFunctionObject))); 2144 | new (functionObjectLocation) TFunctionObject(std::move(fn)); 2145 | 2146 | // pushing the function with the function object as upvalue 2147 | lua_pushcclosure(state, function, 1); 2148 | return PushedObject{state, 1}; 2149 | } 2150 | 2151 | // this is the version of "push" for pointer to functions 2152 | static auto push(lua_State* state, TReturnType (*fn)(TParameters...)) noexcept 2153 | -> PushedObject 2154 | { 2155 | // when the lua script calls the thing we will push on the stack, we want "fn" to be executed 2156 | // since "fn" doesn't need to be destroyed, we simply push it on the stack 2157 | 2158 | // this is the cfunction that is the callback 2159 | const auto function = [](lua_State* state) -> int 2160 | { 2161 | // the function object is an upvalue 2162 | const auto toCall = reinterpret_cast(lua_touserdata(state, lua_upvalueindex(1))); 2163 | return callback(state, toCall, lua_gettop(state)).release(); 2164 | }; 2165 | 2166 | // we copy the function object onto the stack 2167 | lua_pushlightuserdata(state, reinterpret_cast(fn)); 2168 | 2169 | // pushing the function with the function object as upvalue 2170 | lua_pushcclosure(state, function, 1); 2171 | return PushedObject{state, 1}; 2172 | } 2173 | 2174 | // this is the version of "push" for references to functions 2175 | static auto push(lua_State* state, TReturnType (&fn)(TParameters...)) noexcept 2176 | -> PushedObject 2177 | { 2178 | return push(state, &fn); 2179 | } 2180 | 2181 | private: 2182 | // callback that calls the function object 2183 | // this function is used by the callbacks and handles loading arguments from the stack and pushing the return value back 2184 | template 2185 | static auto callback(lua_State* state, TFunctionObject* toCall, int argumentsCount) 2186 | -> PushedObject 2187 | { 2188 | // checking if number of parameters is correct 2189 | if (argumentsCount < LocalFunctionArgumentsCounter::min) { 2190 | // if not, using lua_error to return an error 2191 | luaL_where(state, 1); 2192 | lua_pushstring(state, "This function requires at least "); 2193 | lua_pushnumber(state, LocalFunctionArgumentsCounter::min); 2194 | lua_pushstring(state, " parameter(s)"); 2195 | lua_concat(state, 4); 2196 | luaError(state); 2197 | 2198 | } else if (argumentsCount > LocalFunctionArgumentsCounter::max) { 2199 | // if not, using lua_error to return an error 2200 | luaL_where(state, 1); 2201 | lua_pushstring(state, "This function requires at most "); 2202 | lua_pushnumber(state, LocalFunctionArgumentsCounter::max); 2203 | lua_pushstring(state, " parameter(s)"); 2204 | lua_concat(state, 4); 2205 | luaError(state); 2206 | } 2207 | 2208 | // calling the function 2209 | try { 2210 | return callback2(state, *toCall, argumentsCount); 2211 | 2212 | } catch (const WrongTypeException& ex) { 2213 | // wrong parameter type, using lua_error to return an error 2214 | luaL_where(state, 1); 2215 | lua_pushstring(state, "Unable to convert parameter from "); 2216 | lua_pushstring(state, ex.luaType.c_str()); 2217 | lua_pushstring(state, " to "); 2218 | lua_pushstring(state, ex.destination.name()); 2219 | lua_concat(state, 4); 2220 | luaError(state); 2221 | 2222 | } catch (...) { 2223 | Pusher::push(state, std::current_exception()).release(); 2224 | luaError(state); 2225 | } 2226 | } 2227 | 2228 | template 2229 | static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount) 2230 | -> typename std::enable_if::value && !std::is_void::value, PushedObject>::type 2231 | { 2232 | // pushing the result on the stack and returning number of pushed elements 2233 | typedef Pusher::type> 2234 | P; 2235 | return P::push(state, readIntoFunction(state, tag{}, toCall, -argumentsCount, tag{}...)); 2236 | } 2237 | 2238 | template 2239 | static auto callback2(lua_State* state, TFunctionObject&& toCall, int argumentsCount) 2240 | -> typename std::enable_if::value && !std::is_void::value, PushedObject>::type 2241 | { 2242 | readIntoFunction(state, tag{}, toCall, -argumentsCount, tag{}...); 2243 | return PushedObject{state, 0}; 2244 | } 2245 | }; 2246 | 2247 | // C function pointers 2248 | template 2249 | struct LuaContext::Pusher 2250 | { 2251 | // using the function-pushing implementation 2252 | typedef Pusher 2253 | SubPusher; 2254 | static const int minSize = SubPusher::minSize; 2255 | static const int maxSize = SubPusher::maxSize; 2256 | 2257 | template 2258 | static PushedObject push(lua_State* state, TType value) noexcept { 2259 | return SubPusher::push(state, value); 2260 | } 2261 | }; 2262 | 2263 | // C function references 2264 | template 2265 | struct LuaContext::Pusher 2266 | { 2267 | // using the function-pushing implementation 2268 | typedef Pusher 2269 | SubPusher; 2270 | static const int minSize = SubPusher::minSize; 2271 | static const int maxSize = SubPusher::maxSize; 2272 | 2273 | template 2274 | static PushedObject push(lua_State* state, TType value) noexcept { 2275 | return SubPusher::push(state, value); 2276 | } 2277 | }; 2278 | 2279 | // std::function 2280 | template 2281 | struct LuaContext::Pusher> 2282 | { 2283 | // using the function-pushing implementation 2284 | typedef Pusher 2285 | SubPusher; 2286 | static const int minSize = SubPusher::minSize; 2287 | static const int maxSize = SubPusher::maxSize; 2288 | 2289 | static PushedObject push(lua_State* state, const std::function& value) noexcept { 2290 | return SubPusher::push(state, value); 2291 | } 2292 | }; 2293 | 2294 | // boost::variant 2295 | template 2296 | struct LuaContext::Pusher> 2297 | { 2298 | static const int minSize = PusherMinSize::size; 2299 | static const int maxSize = PusherMaxSize::size; 2300 | 2301 | static PushedObject push(lua_State* state, const boost::variant& value) noexcept { 2302 | PushedObject obj{state, 0}; 2303 | VariantWriter writer{state, obj}; 2304 | value.apply_visitor(writer); 2305 | return std::move(obj); 2306 | } 2307 | 2308 | private: 2309 | struct VariantWriter : public boost::static_visitor<> { 2310 | template 2311 | void operator()(TType value) noexcept 2312 | { 2313 | obj = Pusher::type>::push(state, std::move(value)); 2314 | } 2315 | 2316 | VariantWriter(lua_State* state, PushedObject& obj) : state(state), obj(obj) {} 2317 | lua_State* state; 2318 | PushedObject& obj; 2319 | }; 2320 | }; 2321 | 2322 | // boost::optional 2323 | template 2324 | struct LuaContext::Pusher> { 2325 | typedef Pusher::type> 2326 | UnderlyingPusher; 2327 | 2328 | static const int minSize = UnderlyingPusher::minSize < 1 ? UnderlyingPusher::minSize : 1; 2329 | static const int maxSize = UnderlyingPusher::maxSize > 1 ? UnderlyingPusher::maxSize : 1; 2330 | 2331 | static PushedObject push(lua_State* state, const boost::optional& value) noexcept { 2332 | if (value) { 2333 | return UnderlyingPusher::push(state, value.get()); 2334 | } else { 2335 | lua_pushnil(state); 2336 | return PushedObject{state, 1}; 2337 | } 2338 | } 2339 | }; 2340 | 2341 | // tuple 2342 | template 2343 | struct LuaContext::Pusher> { 2344 | // TODO: NOT EXCEPTION SAFE /!\ // 2345 | static const int minSize = PusherTotalMinSize::size; 2346 | static const int maxSize = PusherTotalMaxSize::size; 2347 | 2348 | static PushedObject push(lua_State* state, const std::tuple& value) noexcept { 2349 | return PushedObject{state, push2(state, value, std::integral_constant{})}; 2350 | } 2351 | 2352 | static PushedObject push(lua_State* state, std::tuple&& value) noexcept { 2353 | return PushedObject{state, push2(state, std::move(value), std::integral_constant{})}; 2354 | } 2355 | 2356 | private: 2357 | template 2358 | static int push2(lua_State* state, const std::tuple& value, std::integral_constant) noexcept { 2359 | typedef typename std::tuple_element>::type ElemType; 2360 | 2361 | return Pusher::type>::push(state, std::get(value)).release() + 2362 | push2(state, value, std::integral_constant{}); 2363 | } 2364 | 2365 | template 2366 | static int push2(lua_State* state, std::tuple&& value, std::integral_constant) noexcept { 2367 | typedef typename std::tuple_element>::type ElemType; 2368 | 2369 | return Pusher::type>::push(state, std::move(std::get(value))).release() + 2370 | push2(state, std::move(value), std::integral_constant{}); 2371 | } 2372 | 2373 | static int push2(lua_State* state, const std::tuple&, std::integral_constant) noexcept { 2374 | return 0; 2375 | } 2376 | 2377 | static int push2(lua_State* state, std::tuple&&, std::integral_constant) noexcept { 2378 | return 0; 2379 | } 2380 | }; 2381 | 2382 | /**************************************************/ 2383 | /* READ FUNCTIONS */ 2384 | /**************************************************/ 2385 | // specializations of the Reader structures 2386 | 2387 | // reading null 2388 | template<> 2389 | struct LuaContext::Reader 2390 | { 2391 | static auto read(lua_State* state, int index) 2392 | -> boost::optional 2393 | { 2394 | if (!lua_isnil(state, index)) 2395 | return boost::none; 2396 | return nullptr; 2397 | } 2398 | }; 2399 | 2400 | // integrals 2401 | template 2402 | struct LuaContext::Reader< 2403 | TType, 2404 | typename std::enable_if::value>::type 2405 | > 2406 | { 2407 | static auto read(lua_State* state, int index) 2408 | -> boost::optional 2409 | { 2410 | # if LUA_VERSION_NUM >= 502 2411 | 2412 | int success; 2413 | auto value = lua_tointegerx(state, index, &success); 2414 | if (success == 0) 2415 | return boost::none; 2416 | return static_cast(value); 2417 | 2418 | # else 2419 | 2420 | if (!lua_isnumber(state, index)) 2421 | return boost::none; 2422 | return static_cast(lua_tointeger(state, index)); 2423 | 2424 | # endif 2425 | } 2426 | }; 2427 | 2428 | // floating points 2429 | template 2430 | struct LuaContext::Reader< 2431 | TType, 2432 | typename std::enable_if::value>::type 2433 | > 2434 | { 2435 | static auto read(lua_State* state, int index) 2436 | -> boost::optional 2437 | { 2438 | # if LUA_VERSION_NUM >= 502 2439 | 2440 | int success; 2441 | auto value = lua_tonumberx(state, index, &success); 2442 | if (success == 0) 2443 | return boost::none; 2444 | return static_cast(value); 2445 | 2446 | # else 2447 | 2448 | if (!lua_isnumber(state, index)) 2449 | return boost::none; 2450 | return static_cast(lua_tonumber(state, index)); 2451 | 2452 | # endif 2453 | } 2454 | }; 2455 | 2456 | // boolean 2457 | template<> 2458 | struct LuaContext::Reader 2459 | { 2460 | static auto read(lua_State* state, int index) 2461 | -> boost::optional 2462 | { 2463 | if (!lua_isboolean(state, index)) 2464 | return boost::none; 2465 | return lua_toboolean(state, index) != 0; 2466 | } 2467 | }; 2468 | 2469 | // string 2470 | // lua_tostring returns a temporary pointer, but that's not a problem since we copy 2471 | // the data into a std::string 2472 | template<> 2473 | struct LuaContext::Reader 2474 | { 2475 | static auto read(lua_State* state, int index) 2476 | -> boost::optional 2477 | { 2478 | const auto val = lua_tostring(state, index); 2479 | if (val == 0) 2480 | return boost::none; 2481 | return std::string(val); 2482 | } 2483 | }; 2484 | 2485 | // enums 2486 | template 2487 | struct LuaContext::Reader< 2488 | TType, 2489 | typename std::enable_if::value>::type 2490 | > 2491 | { 2492 | static auto read(lua_State* state, int index) 2493 | -> boost::optional 2494 | { 2495 | if (!lua_isnumber(state, index) != 0 || fmod(lua_tonumber(state, index), 1.) != 0) 2496 | return boost::none; 2497 | return static_cast(lua_tointeger(state, index)); 2498 | } 2499 | }; 2500 | 2501 | // LuaFunctionCaller 2502 | template 2503 | struct LuaContext::Reader> 2504 | { 2505 | typedef LuaFunctionCaller 2506 | ReturnType; 2507 | 2508 | static auto read(lua_State* state, int index) 2509 | -> boost::optional 2510 | { 2511 | if (lua_isfunction(state, index) == 0 && lua_isuserdata(state, index) == 0) 2512 | return boost::none; 2513 | return ReturnType(state); 2514 | } 2515 | }; 2516 | 2517 | // function 2518 | template 2519 | struct LuaContext::Reader> 2520 | { 2521 | static auto read(lua_State* state, int index) 2522 | -> boost::optional> 2523 | { 2524 | if (auto val = Reader>::read(state, index)) 2525 | { 2526 | std::function f{*val}; 2527 | return boost::optional>{std::move(f)}; 2528 | } 2529 | 2530 | return boost::none; 2531 | } 2532 | }; 2533 | 2534 | // vector of pairs 2535 | template 2536 | struct LuaContext::Reader>> 2537 | { 2538 | static auto read(lua_State* state, int index) 2539 | -> boost::optional>> 2540 | { 2541 | if (!lua_istable(state, index)) 2542 | return boost::none; 2543 | 2544 | std::vector> result; 2545 | 2546 | // we traverse the table at the top of the stack 2547 | lua_pushnil(state); // first key 2548 | while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { 2549 | // now a key and its value are pushed on the stack 2550 | try { 2551 | auto val1 = Reader::read(state, -2); 2552 | auto val2 = Reader::read(state, -1); 2553 | 2554 | if (!val1.is_initialized() || !val2.is_initialized()) { 2555 | lua_pop(state, 2); // we remove the value and the key 2556 | return {}; 2557 | } 2558 | 2559 | result.push_back({ std::move(val1.get()), std::move(val2.get()) }); 2560 | lua_pop(state, 1); // we remove the value but keep the key for the next iteration 2561 | 2562 | } catch(...) { 2563 | lua_pop(state, 2); // we remove the value and the key 2564 | return {}; 2565 | } 2566 | } 2567 | 2568 | return { std::move(result) }; 2569 | } 2570 | }; 2571 | 2572 | // map 2573 | template 2574 | struct LuaContext::Reader> 2575 | { 2576 | static auto read(lua_State* state, int index) 2577 | -> boost::optional> 2578 | { 2579 | if (!lua_istable(state, index)) 2580 | return boost::none; 2581 | 2582 | std::map result; 2583 | 2584 | // we traverse the table at the top of the stack 2585 | lua_pushnil(state); // first key 2586 | while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { 2587 | // now a key and its value are pushed on the stack 2588 | try { 2589 | auto key = Reader::read(state, -2); 2590 | auto value = Reader::read(state, -1); 2591 | 2592 | if (!key.is_initialized() || !value.is_initialized()) { 2593 | lua_pop(state, 2); // we remove the value and the key 2594 | return {}; 2595 | } 2596 | 2597 | result.insert({ std::move(key.get()), std::move(value.get()) }); 2598 | lua_pop(state, 1); // we remove the value but keep the key for the next iteration 2599 | 2600 | } catch(...) { 2601 | lua_pop(state, 2); // we remove the value and the key 2602 | return {}; 2603 | } 2604 | } 2605 | 2606 | return { std::move(result) }; 2607 | } 2608 | }; 2609 | 2610 | // unordered_map 2611 | template 2612 | struct LuaContext::Reader> 2613 | { 2614 | static auto read(lua_State* state, int index) 2615 | -> boost::optional> 2616 | { 2617 | if (!lua_istable(state, index)) 2618 | return boost::none; 2619 | 2620 | std::unordered_map result; 2621 | 2622 | // we traverse the table at the top of the stack 2623 | lua_pushnil(state); // first key 2624 | while (lua_next(state, (index > 0) ? index : (index - 1)) != 0) { 2625 | // now a key and its value are pushed on the stack 2626 | try { 2627 | auto key = Reader::read(state, -2); 2628 | auto value = Reader::read(state, -1); 2629 | 2630 | if (!key.is_initialized() || !value.is_initialized()) { 2631 | lua_pop(state, 2); // we remove the value and the key 2632 | return {}; 2633 | } 2634 | 2635 | result.insert({ std::move(key.get()), std::move(value.get()) }); 2636 | lua_pop(state, 1); // we remove the value but keep the key for the next iteration 2637 | 2638 | } catch(...) { 2639 | lua_pop(state, 2); // we remove the value and the key 2640 | return {}; 2641 | } 2642 | } 2643 | 2644 | return { std::move(result) }; 2645 | } 2646 | }; 2647 | 2648 | // optional 2649 | // IMPORTANT: optional means "either nil or the value of the right type" 2650 | // * if the value is nil, then an optional containing an empty optional is returned 2651 | // * if the value is of the right type, then an optional containing an optional containing the value is returned 2652 | // * if the value is of the wrong type, then an empty optional is returned 2653 | template 2654 | struct LuaContext::Reader> 2655 | { 2656 | static auto read(lua_State* state, int index) 2657 | -> boost::optional> 2658 | { 2659 | if (lua_isnil(state, index)) 2660 | return boost::optional{boost::none}; 2661 | if (auto&& other = Reader::read(state, index)) 2662 | return std::move(other); 2663 | return boost::none; 2664 | } 2665 | }; 2666 | 2667 | // variant 2668 | template 2669 | struct LuaContext::Reader> 2670 | { 2671 | typedef boost::variant 2672 | ReturnType; 2673 | 2674 | private: 2675 | // class doing operations for a range of types from TIterBegin to TIterEnd 2676 | template 2677 | struct VariantReader 2678 | { 2679 | using SubReader = Reader::type>::type>; 2680 | 2681 | static auto read(lua_State* state, int index) 2682 | -> boost::optional 2683 | { 2684 | // note: using SubReader::read triggers a compilation error when used with a reference 2685 | if (const auto val = SubReader::read(state, index)) 2686 | return boost::variant{*val}; 2687 | return VariantReader::type, TIterEnd>::read(state, index); 2688 | } 2689 | }; 2690 | 2691 | // specialization of class above being called when list of remaining types is empty 2692 | template 2693 | struct VariantReader::type::value == 0>::type> 2694 | { 2695 | static auto read(lua_State* state, int index) 2696 | -> boost::optional 2697 | { 2698 | return boost::none; 2699 | } 2700 | }; 2701 | 2702 | // this is the main type 2703 | typedef VariantReader::type, typename boost::mpl::end::type> 2704 | MainVariantReader; 2705 | 2706 | public: 2707 | static auto read(lua_State* state, int index) 2708 | -> boost::optional 2709 | { 2710 | return MainVariantReader::read(state, index); 2711 | } 2712 | }; 2713 | 2714 | // reading a tuple 2715 | // tuple have an additional argument for their functions, that is the maximum size to read 2716 | // if maxSize is smaller than the tuple size, then the remaining parameters will be left to default value 2717 | template<> 2718 | struct LuaContext::Reader> 2719 | { 2720 | static auto read(lua_State* state, int index, int maxSize = 0) 2721 | -> boost::optional> 2722 | { 2723 | return std::tuple<>{}; 2724 | } 2725 | }; 2726 | 2727 | template 2728 | struct LuaContext::Reader, 2729 | typename std::enable_if::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler 2730 | > 2731 | { 2732 | // this is the "TFirst is NOT default constructible" version 2733 | 2734 | typedef std::tuple 2735 | ReturnType; 2736 | 2737 | static auto read(lua_State* state, int index, int maxSize = std::tuple_size::value) 2738 | -> boost::optional 2739 | { 2740 | if (maxSize <= 0) 2741 | return boost::none; 2742 | 2743 | auto firstVal = Reader::read(state, index); 2744 | auto othersVal = Reader>::read(state, index + 1, maxSize - 1); 2745 | 2746 | if (!firstVal || !othersVal) 2747 | return boost::none; 2748 | 2749 | return std::tuple_cat(std::tuple(*firstVal), std::move(*othersVal)); 2750 | } 2751 | }; 2752 | 2753 | template 2754 | struct LuaContext::Reader, 2755 | typename std::enable_if::value>::type // TODO: replace by std::is_default_constructible when it works on every compiler 2756 | > 2757 | { 2758 | // this is the "TFirst is default-constructible" version 2759 | 2760 | typedef std::tuple 2761 | ReturnType; 2762 | 2763 | static auto read(lua_State* state, int index, int maxSize = std::tuple_size::value) 2764 | -> boost::optional 2765 | { 2766 | auto othersVal = Reader>::read(state, index + 1, maxSize - 1); 2767 | if (!othersVal) 2768 | return boost::none; 2769 | 2770 | if (maxSize <= 0) 2771 | return std::tuple_cat(std::tuple(), std::move(*othersVal)); 2772 | 2773 | auto firstVal = Reader::read(state, index); 2774 | if (!firstVal) 2775 | return boost::none; 2776 | 2777 | return std::tuple_cat(std::tuple(*firstVal), std::move(*othersVal)); 2778 | } 2779 | }; 2780 | 2781 | #endif 2782 | -------------------------------------------------------------------------------- /include/misc/exception.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Pierre KRIEGER 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /** 29 | * This file is a replacement for the missing C++11 features of MSVC++ 30 | */ 31 | #ifndef INCLUDE_EXCEPTION_HPP 32 | #define INCLUDE_EXCEPTION_HPP 33 | 34 | #include 35 | #include 36 | 37 | #ifdef _MSC_VER 38 | # define EXCEPTION_HPP_NORETURN_MACRO __declspec(noreturn) 39 | #else 40 | # define EXCEPTION_HPP_NORETURN_MACRO [[noreturn]] 41 | #endif 42 | 43 | namespace std { 44 | class nested_exception { 45 | public: 46 | nested_exception() : nested(current_exception()) {} 47 | virtual ~nested_exception() {} 48 | 49 | EXCEPTION_HPP_NORETURN_MACRO 50 | void rethrow_nested() const { std::rethrow_exception(nested); } 51 | 52 | exception_ptr nested_ptr() const { return nested; } 53 | 54 | 55 | private: 56 | exception_ptr nested; 57 | }; 58 | 59 | template 60 | EXCEPTION_HPP_NORETURN_MACRO 61 | void throw_with_nested(T&& t) 62 | { 63 | typedef remove_reference::type RealT; 64 | 65 | struct ThrowWithNestedExcept : nested_exception, RealT 66 | { 67 | ThrowWithNestedExcept(T&& t) : RealT(std::forward(t)) {} 68 | }; 69 | 70 | if (is_base_of::value) 71 | throw std::forward(t); 72 | else 73 | throw ThrowWithNestedExcept(std::forward(t)); 74 | } 75 | 76 | template 77 | void rethrow_if_nested(const E& e) 78 | { 79 | const auto ptr = dynamic_cast(&e); 80 | if (ptr) ptr->rethrow_nested(); 81 | } 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /tests/advanced_readwrite.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(AdvancedReadWrite, WritingVariant) { 5 | LuaContext context; 6 | 7 | boost::variant val{std::string{"test"}}; 8 | context.writeVariable("a", val); 9 | EXPECT_EQ("test", context.readVariable("a")); 10 | } 11 | 12 | TEST(AdvancedReadWrite, ReadingVariant) { 13 | LuaContext context; 14 | 15 | context.writeVariable("a", "test"); 16 | 17 | const auto val = context.readVariable>("a"); 18 | EXPECT_EQ(2, val.which()); 19 | EXPECT_EQ("test", boost::get(val)); 20 | } 21 | 22 | TEST(AdvancedReadWrite, VariantError) { 23 | LuaContext context; 24 | 25 | context.writeVariable("a", "test"); 26 | EXPECT_THROW((context.readVariable>>("a")), LuaContext::WrongTypeException); 27 | } 28 | 29 | TEST(AdvancedReadWrite, ReadingOptional) { 30 | LuaContext context; 31 | 32 | context.writeVariable("a", 3); 33 | context.writeVariable("b", "test"); 34 | 35 | EXPECT_EQ(3, context.readVariable>("a").get()); 36 | EXPECT_THROW(context.readVariable>("b"), LuaContext::WrongTypeException); 37 | EXPECT_FALSE(context.readVariable>("c").is_initialized()); 38 | } 39 | 40 | TEST(AdvancedReadWrite, EmptyArray) { 41 | LuaContext context; 42 | 43 | context.writeVariable("a", LuaContext::EmptyArray); 44 | EXPECT_EQ("table", context.executeCode("return type(a)")); 45 | } 46 | 47 | TEST(AdvancedReadWrite, ReadInsideArrays) { 48 | LuaContext context; 49 | 50 | context.executeCode("a = { 12, 34 }"); 51 | EXPECT_EQ(12, context.readVariable("a", 1)); 52 | EXPECT_EQ(34, context.readVariable("a", 2)); 53 | } 54 | 55 | TEST(AdvancedReadWrite, WriteVariableInsideArrays) { 56 | LuaContext context; 57 | 58 | context.executeCode("a = { 1, {} }"); 59 | context.writeVariable("a", 1, 34); 60 | context.writeVariable("a", 2, "test", 14); 61 | EXPECT_EQ(34, context.readVariable("a", 1)); 62 | EXPECT_EQ(14, context.readVariable("a", 2, "test")); 63 | } 64 | 65 | TEST(AdvancedReadWrite, WriteFunctionInsideArrays) { 66 | LuaContext context; 67 | 68 | context.executeCode("a = { 1, {} }"); 69 | context.writeFunction("a", 1, [](int x) { return x + 1; }); 70 | context.writeFunction("a", 2, "test", [](int x) { return x * 2; }); 71 | EXPECT_EQ(34, context.executeCode("local f = a[1]; return f(33)")); 72 | EXPECT_EQ(14, context.executeCode("local f = a[2].test; return f(7)")); 73 | } 74 | 75 | TEST(AdvancedReadWrite, WritingVectors) { 76 | LuaContext context; 77 | 78 | context.writeVariable("a", std::vector{"hello", "world"}); 79 | 80 | EXPECT_EQ("hello", context.readVariable("a", 1)); 81 | EXPECT_EQ("world", context.readVariable("a", 2)); 82 | 83 | const auto val = context.readVariable>("a"); 84 | EXPECT_EQ("hello", val.at(1)); 85 | EXPECT_EQ("world", val.at(2)); 86 | } 87 | 88 | TEST(AdvancedReadWrite, VectorOfPairs) { 89 | LuaContext context; 90 | 91 | context.writeVariable("a", std::vector>{ 92 | { 1, "hello" }, 93 | { -23, "world" } 94 | }); 95 | 96 | EXPECT_EQ("hello", context.readVariable("a", 1)); 97 | EXPECT_EQ("world", context.readVariable("a", -23)); 98 | 99 | const auto val = context.readVariable>>("a"); 100 | EXPECT_TRUE(val[0].first == 1 || val[0].first == -23); 101 | EXPECT_TRUE(val[1].first == 1 || val[1].first == -23); 102 | EXPECT_TRUE(val[0].second == "hello" || val[0].second == "world"); 103 | EXPECT_TRUE(val[1].second == "hello" || val[1].second == "world"); 104 | } 105 | 106 | TEST(AdvancedReadWrite, Maps) { 107 | LuaContext context; 108 | 109 | context.writeVariable("a", std::map{ 110 | { 1, "hello" }, 111 | { -23, "world" } 112 | }); 113 | 114 | context.executeCode("b = { \"hello\", \"world\" }"); 115 | 116 | EXPECT_EQ("hello", context.readVariable("a", 1)); 117 | EXPECT_EQ("world", context.readVariable("a", -23)); 118 | 119 | EXPECT_EQ("hello", context.readVariable("b", 1)); 120 | EXPECT_EQ("world", context.readVariable("b", 2)); 121 | 122 | const auto val = context.readVariable>("a"); 123 | EXPECT_EQ("hello", val.at(1)); 124 | EXPECT_EQ("world", val.at(-23)); 125 | 126 | const auto b = context.readVariable>("b"); 127 | EXPECT_EQ("hello", b.at(1)); 128 | EXPECT_EQ("world", b.at(2)); 129 | } 130 | 131 | TEST(AdvancedReadWrite, UnorderedMaps) { 132 | LuaContext context; 133 | 134 | context.writeVariable("a", std::unordered_map{ 135 | { 1, "hello" }, 136 | { -23, "world" } 137 | }); 138 | 139 | EXPECT_EQ("hello", context.readVariable("a", 1)); 140 | EXPECT_EQ("world", context.readVariable("a", -23)); 141 | 142 | const auto val = context.readVariable>("a"); 143 | EXPECT_EQ("hello", val.at(1)); 144 | EXPECT_EQ("world", val.at(-23)); 145 | } 146 | 147 | TEST(AdvancedReadWrite, WritingOptionals) { 148 | LuaContext context; 149 | 150 | context.writeVariable("a", boost::optional{}); 151 | context.writeVariable("b", boost::optional{12}); 152 | 153 | EXPECT_EQ("nil", context.executeCode("return type(a)")); 154 | EXPECT_EQ("number", context.executeCode("return type(b)")); 155 | EXPECT_EQ(12, context.executeCode("return b")); 156 | } 157 | 158 | TEST(AdvancedReadWrite, AdvancedExample) { 159 | LuaContext context; 160 | 161 | context.writeVariable("a", 162 | std::vector< std::pair< boost::variant, boost::variant >> 163 | { 164 | { "test", true }, 165 | { 2, 6.4f }, 166 | { "hello", 1.f }, 167 | { "world", -7.6f }, 168 | { 18, false } 169 | } 170 | ); 171 | 172 | EXPECT_EQ(true, context.executeCode("return a.test")); 173 | EXPECT_DOUBLE_EQ(6.4f, context.executeCode("return a[2]")); 174 | EXPECT_DOUBLE_EQ(-7.6f, context.readVariable("a", "world")); 175 | } 176 | -------------------------------------------------------------------------------- /tests/basic_readwrite.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(BasicReadWrite, Integers) { 5 | LuaContext context; 6 | 7 | context.writeVariable("a", 5); 8 | EXPECT_EQ(5, context.readVariable("a")); 9 | 10 | context.writeVariable("b", -397); 11 | EXPECT_EQ(-397, context.readVariable("b")); 12 | } 13 | 14 | TEST(BasicReadWrite, Doubles) { 15 | LuaContext context; 16 | 17 | context.writeVariable("a", 5.4); 18 | EXPECT_DOUBLE_EQ(5.4, context.readVariable("a")); 19 | 20 | context.writeVariable("b", -6.72f); 21 | EXPECT_DOUBLE_EQ(-6.72f, context.readVariable("b")); 22 | } 23 | 24 | TEST(BasicReadWrite, Booleans) { 25 | LuaContext context; 26 | 27 | context.writeVariable("a", true); 28 | EXPECT_TRUE(context.readVariable("a")); 29 | 30 | context.writeVariable("b", false); 31 | EXPECT_FALSE(context.readVariable("b")); 32 | } 33 | 34 | TEST(BasicReadWrite, Strings) { 35 | LuaContext context; 36 | 37 | context.writeVariable("a", "hello"); 38 | EXPECT_EQ("hello", context.readVariable("a")); 39 | 40 | context.writeVariable("b", "world"); 41 | EXPECT_EQ("world", context.readVariable("b")); 42 | } 43 | 44 | TEST(BasicReadWrite, Enums) { 45 | enum class Foo { 46 | A, 47 | B, 48 | C 49 | }; 50 | 51 | LuaContext context; 52 | 53 | context.writeVariable("a", Foo::A); 54 | EXPECT_EQ(Foo::A, context.readVariable("a")); 55 | 56 | context.writeVariable("b", Foo::B); 57 | EXPECT_EQ(Foo::B, context.readVariable("b")); 58 | } 59 | 60 | TEST(BasicReadWrite, Conversions) { 61 | LuaContext context; 62 | 63 | context.writeVariable("a", "12"); 64 | EXPECT_EQ(12, context.readVariable("a")); 65 | 66 | context.writeVariable("b", 24); 67 | EXPECT_EQ("24", context.readVariable("b")); 68 | } 69 | 70 | TEST(BasicReadWrite, TypeError) { 71 | struct Foo {}; 72 | 73 | LuaContext context; 74 | 75 | context.writeVariable("a", "hello"); 76 | EXPECT_THROW(context.readVariable("a"), LuaContext::WrongTypeException); 77 | EXPECT_THROW(context.readVariable("a"), LuaContext::WrongTypeException); 78 | EXPECT_THROW(context.readVariable("a"), LuaContext::WrongTypeException); 79 | EXPECT_THROW(context.readVariable>("a"), LuaContext::WrongTypeException); 80 | EXPECT_THROW(context.readVariable("a"), LuaContext::WrongTypeException); 81 | EXPECT_THROW(context.readVariable>("a"), LuaContext::WrongTypeException); 82 | } 83 | -------------------------------------------------------------------------------- /tests/custom_types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(CustomTypes, ReadWrite) { 5 | struct Object { 6 | int value; 7 | }; 8 | 9 | LuaContext context; 10 | context.writeVariable("obj", Object{5}); 11 | EXPECT_EQ(5, context.readVariable("obj").value); 12 | } 13 | 14 | TEST(CustomTypes, ReadReference) { 15 | struct Object { 16 | int value; 17 | }; 18 | 19 | LuaContext context; 20 | context.writeVariable("obj", Object{5}); 21 | context.readVariable("obj").value = 12; 22 | EXPECT_EQ(12, context.readVariable("obj").value); 23 | } 24 | 25 | TEST(CustomTypes, MemberFunctions) { 26 | struct Object { 27 | void increment() { ++value; } 28 | int value; 29 | }; 30 | 31 | LuaContext context; 32 | context.registerFunction("increment", &Object::increment); 33 | 34 | context.writeVariable("obj", Object{10}); 35 | context.executeCode("obj:increment()"); 36 | 37 | EXPECT_EQ(11, context.readVariable("obj").value); 38 | } 39 | 40 | TEST(CustomTypes, ConstMemberFunctionsWithRawPointers) { 41 | struct Object { 42 | int foo() { return 1; } 43 | int fooC() const { return 2; } 44 | }; 45 | 46 | LuaContext context; 47 | context.registerFunction("foo", &Object::foo); 48 | context.registerFunction("fooC", &Object::fooC); 49 | 50 | Object obj; 51 | context.writeVariable("obj", &obj); 52 | context.writeVariable("objC", const_cast(&obj)); 53 | 54 | EXPECT_EQ(2, context.executeCode("return obj:fooC()")); 55 | EXPECT_EQ(1, context.executeCode("return obj:foo()")); 56 | EXPECT_EQ(2, context.executeCode("return objC:fooC()")); 57 | EXPECT_ANY_THROW(context.executeCode("return objC:foo()")); 58 | } 59 | 60 | TEST(CustomTypes, ConstMemberFunctionsWithSharedPointers) { 61 | struct Object { 62 | int foo() { return 1; } 63 | int fooC() const { return 2; } 64 | }; 65 | 66 | LuaContext context; 67 | context.registerFunction("foo", &Object::foo); 68 | context.registerFunction("fooC", &Object::fooC); 69 | 70 | auto obj = std::make_shared(); 71 | context.writeVariable("obj", obj); 72 | context.writeVariable("objC", std::shared_ptr(obj)); 73 | 74 | EXPECT_EQ(2, context.executeCode("return obj:fooC()")); 75 | EXPECT_EQ(1, context.executeCode("return obj:foo()")); 76 | EXPECT_EQ(2, context.executeCode("return objC:fooC()")); 77 | EXPECT_ANY_THROW(context.executeCode("return objC:foo()")); 78 | } 79 | 80 | TEST(CustomTypes, ConstVolatileMemberFunctions) { 81 | struct Object { 82 | int foo() { return 1; } 83 | int fooC() const { return 2; } 84 | int fooV() volatile { return 3; } 85 | int fooCV() const volatile { return 4; } 86 | }; 87 | 88 | LuaContext context; 89 | context.registerFunction("foo", &Object::foo); 90 | context.registerFunction("fooC", &Object::fooC); 91 | context.registerFunction("fooV", &Object::fooV); 92 | context.registerFunction("fooCV", &Object::fooCV); 93 | 94 | context.writeVariable("obj", Object{}); 95 | 96 | EXPECT_EQ(1, context.executeCode("return obj:foo()")); 97 | EXPECT_EQ(2, context.executeCode("return obj:fooC()")); 98 | EXPECT_EQ(3, context.executeCode("return obj:fooV()")); 99 | EXPECT_EQ(4, context.executeCode("return obj:fooCV()")); 100 | } 101 | 102 | TEST(CustomTypes, MemberFunctionsReturnedObjects) { 103 | struct Object { 104 | int add(int x) { return x + 2; } 105 | }; 106 | 107 | LuaContext context; 108 | context.registerFunction("add", &Object::add); 109 | 110 | context.writeVariable("Object", LuaContext::EmptyArray); 111 | context.writeFunction("Object", "newRaw", []() { return Object{}; }); 112 | Object obj; 113 | context.writeFunction("Object", "newPtr", [&]() { return &obj; }); 114 | context.writeFunction("Object", "newSharedPtr", []() { return std::make_shared(); }); 115 | 116 | EXPECT_EQ(12, context.executeCode("return Object.newRaw():add(10)")); 117 | EXPECT_EQ(17, context.executeCode("return Object.newPtr():add(15)")); 118 | EXPECT_EQ(22, context.executeCode("return Object.newSharedPtr():add(20)")); 119 | } 120 | 121 | TEST(CustomTypes, MembersPlain) { 122 | struct Object { 123 | int value; 124 | }; 125 | 126 | LuaContext context; 127 | context.registerMember("value", &Object::value); 128 | 129 | context.writeVariable("obj", Object{10}); 130 | context.executeCode("obj.value = obj.value + 5"); 131 | 132 | EXPECT_EQ(15, context.readVariable("obj").value); 133 | } 134 | 135 | TEST(CustomTypes, MembersPointers) { 136 | struct Object { 137 | int value; 138 | }; 139 | 140 | LuaContext context; 141 | context.registerMember("value", &Object::value); 142 | 143 | Object obj{10}; 144 | context.writeVariable("obj", &obj); 145 | context.executeCode("obj.value = obj.value + 5"); 146 | EXPECT_EQ(15, obj.value); 147 | 148 | context.writeVariable("obj2", const_cast(&obj)); 149 | EXPECT_EQ(15, context.executeCode("return obj2.value")); 150 | EXPECT_ANY_THROW(context.executeCode("obj2.value = 12")); 151 | } 152 | 153 | TEST(CustomTypes, MembersSharedPointers) { 154 | struct Object { 155 | int value; 156 | }; 157 | 158 | LuaContext context; 159 | context.registerMember("value", &Object::value); 160 | 161 | auto obj = std::make_shared(); 162 | obj->value = 10; 163 | context.writeVariable("obj", obj); 164 | context.executeCode("obj.value = obj.value + 5"); 165 | EXPECT_EQ(15, obj->value); 166 | 167 | context.writeVariable("obj2", std::shared_ptr(obj)); 168 | EXPECT_EQ(15, context.executeCode("return obj2.value")); 169 | EXPECT_ANY_THROW(context.executeCode("obj2.value = 12")); 170 | } 171 | 172 | TEST(CustomTypes, CustomMemberFunctions) { 173 | struct Object { 174 | Object(int v) : value(v) {} 175 | int value; 176 | }; 177 | 178 | LuaContext context; 179 | context.registerFunction("increment", [](Object& obj) { ++obj.value; }); 180 | context.registerFunction("add", [](Object& obj, int x) { obj.value += x; return obj.value; }); 181 | 182 | context.writeVariable("obj1", Object{10}); 183 | Object obj{10}; 184 | context.writeVariable("obj2", &obj); 185 | context.writeVariable("obj3", std::make_shared(10)); 186 | 187 | context.executeCode("obj1:increment()"); 188 | context.executeCode("obj2:increment()"); 189 | context.executeCode("obj3:increment()"); 190 | 191 | EXPECT_EQ(11, context.readVariable("obj1").value); 192 | EXPECT_EQ(11, context.readVariable("obj2")->value); 193 | EXPECT_EQ(11, obj.value); 194 | EXPECT_EQ(11, context.readVariable>("obj3")->value); 195 | 196 | EXPECT_EQ(14, context.executeCode("return obj1:add(3)")); 197 | EXPECT_EQ(14, context.executeCode("return obj2:add(3)")); 198 | EXPECT_EQ(14, context.executeCode("return obj3:add(3)")); 199 | } 200 | 201 | TEST(CustomTypes, CustomMemberFunctionsCustomFunctionObject) { 202 | struct Object { 203 | Object(int v) : value(v) {} 204 | int value; 205 | }; 206 | 207 | struct ObjectIncrementer { 208 | void operator()(Object& obj) const { ++obj.value; } 209 | }; 210 | 211 | LuaContext context; 212 | context.registerFunction("increment1", ObjectIncrementer{}); 213 | context.registerFunction("increment2", ObjectIncrementer{}); 214 | 215 | context.writeVariable("obj1", Object{ 10 }); 216 | Object obj{ 10 }; 217 | context.writeVariable("obj2", &obj); 218 | context.writeVariable("obj3", std::make_shared(10)); 219 | 220 | context.executeCode("obj1:increment1()"); 221 | context.executeCode("obj1:increment2()"); 222 | context.executeCode("obj2:increment1()"); 223 | context.executeCode("obj2:increment2()"); 224 | context.executeCode("obj3:increment1()"); 225 | context.executeCode("obj3:increment2()"); 226 | 227 | EXPECT_EQ(12, context.readVariable("obj1").value); 228 | EXPECT_EQ(12, context.readVariable("obj2")->value); 229 | EXPECT_EQ(12, context.readVariable>("obj3")->value); 230 | } 231 | 232 | TEST(CustomTypes, CustomMembers) { 233 | struct Object {}; 234 | 235 | LuaContext context; 236 | context.registerMember("value", 237 | [](const Object& obj) { return 2; }, 238 | [](Object& obj, int val) {} 239 | ); 240 | 241 | context.writeVariable("obj", Object{}); 242 | EXPECT_EQ(2, context.executeCode("return obj.value")); 243 | } 244 | 245 | TEST(CustomTypes, Unregistering) { 246 | struct Object { 247 | int foo() { return 2; } 248 | }; 249 | 250 | LuaContext context; 251 | context.writeVariable("obj", Object{}); 252 | EXPECT_ANY_THROW(context.executeCode("return obj:foo()")); 253 | 254 | context.registerFunction("foo", &Object::foo); 255 | EXPECT_EQ(2, context.executeCode("return obj:foo()")); 256 | 257 | context.unregisterFunction("foo"); 258 | EXPECT_ANY_THROW(context.executeCode("return obj:foo()")); 259 | } 260 | 261 | TEST(CustomTypes, GenericMembers) { 262 | struct Object { 263 | int value = 5; 264 | }; 265 | 266 | LuaContext context; 267 | context.registerMember( 268 | [](const Object& obj, const std::string& name) { return obj.value; }, 269 | [](Object& obj, const std::string& name, int val) { obj.value = val; } 270 | ); 271 | 272 | context.writeVariable("obj", Object{}); 273 | EXPECT_EQ(5, context.executeCode("return obj.foo")); 274 | context.executeCode("obj.bar = 18"); 275 | EXPECT_EQ(18, context.executeCode("return obj.bowl")); 276 | } 277 | 278 | TEST(CustomTypes, CopiesCheckReadWrite) { 279 | int copiesCount = 0; 280 | int movesCount = 0; 281 | 282 | struct Foo { 283 | Foo(int* copiesCount, int* movesCount) : copiesCount(copiesCount), movesCount(movesCount) {} 284 | Foo(const Foo& f) : copiesCount(f.copiesCount), movesCount(f.movesCount) { ++*copiesCount; } 285 | Foo(Foo&& f) : copiesCount(f.copiesCount), movesCount(f.movesCount) { ++*movesCount; } 286 | 287 | int* copiesCount; 288 | int* movesCount; 289 | }; 290 | 291 | 292 | LuaContext context; 293 | context.writeVariable("obj", Foo{&copiesCount, &movesCount}); 294 | EXPECT_EQ(0, copiesCount); 295 | EXPECT_EQ(1, movesCount); 296 | 297 | context.readVariable("obj"); 298 | EXPECT_EQ(1, copiesCount); 299 | EXPECT_EQ(1, movesCount); 300 | 301 | context.executeCode("a = obj"); 302 | EXPECT_EQ(1, copiesCount); 303 | EXPECT_EQ(1, movesCount); 304 | 305 | context.readVariable("obj"); 306 | EXPECT_EQ(1, copiesCount); 307 | EXPECT_EQ(1, movesCount); 308 | } 309 | 310 | TEST(CustomTypes, CopiesCheckReturnByValue) { 311 | int copiesCount = 0; 312 | int movesCount = 0; 313 | 314 | struct Foo { 315 | Foo(int* copiesCount, int* movesCount) : copiesCount(copiesCount), movesCount(movesCount) {} 316 | Foo(const Foo& f) : copiesCount(f.copiesCount), movesCount(f.movesCount) { ++*copiesCount; } 317 | Foo(Foo&& f) : copiesCount(f.copiesCount), movesCount(f.movesCount) { ++*movesCount; } 318 | 319 | int* copiesCount; 320 | int* movesCount; 321 | }; 322 | 323 | 324 | LuaContext context; 325 | context.writeFunction("build", [&]() { return Foo{&copiesCount, &movesCount}; }); 326 | 327 | context.executeCode("obj = build()"); 328 | EXPECT_EQ(0, copiesCount); 329 | EXPECT_EQ(movesCount, 1); 330 | } 331 | -------------------------------------------------------------------------------- /tests/execution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(Execution, BasicExecution) { 5 | LuaContext context; 6 | context.executeCode("a = 2"); 7 | EXPECT_EQ(2, context.readVariable("a")); 8 | } 9 | 10 | TEST(Execution, Errors) { 11 | LuaContext context; 12 | EXPECT_THROW(context.executeCode("qsdfqsdf"), LuaContext::SyntaxErrorException); 13 | 14 | context.writeFunction("f", [](bool) {}); 15 | EXPECT_THROW(context.executeCode("f('hello')"), LuaContext::ExecutionErrorException); 16 | } 17 | 18 | TEST(Execution, ReturningValues) { 19 | LuaContext context; 20 | EXPECT_EQ(2, context.executeCode("return 2")); 21 | EXPECT_EQ("hello", context.executeCode("return 'hello'")); 22 | EXPECT_EQ(true, context.executeCode("return true")); 23 | 24 | const auto f = context.executeCode>("return function(x) return x + 1; end"); 25 | EXPECT_EQ(5, f(4)); 26 | } 27 | 28 | TEST(Execution, ReturningMultipleValues) { 29 | LuaContext context; 30 | 31 | const auto values = context.executeCode>("return 2, 'hello', 5"); 32 | EXPECT_EQ(2, std::get<0>(values)); 33 | EXPECT_EQ("hello", std::get<1>(values)); 34 | EXPECT_EQ(5, std::get<2>(values)); 35 | } 36 | -------------------------------------------------------------------------------- /tests/functions_read.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(FunctionsRead, NativeFunctions) { 5 | struct Foo { 6 | static int increment(int x) 7 | { 8 | return x + 1; 9 | } 10 | }; 11 | 12 | LuaContext context; 13 | 14 | context.writeVariable("f", &Foo::increment); 15 | 16 | const auto f = context.readVariable>("f"); 17 | EXPECT_EQ(4, f(3)); 18 | 19 | const auto g = context.readVariable>("f"); 20 | EXPECT_EQ(4, g(3)); 21 | } 22 | 23 | TEST(FunctionsRead, Lambdas) { 24 | LuaContext context; 25 | 26 | context.writeFunction("f", [](int x) { return x + 1; }); 27 | 28 | const auto f = context.readVariable>("f"); 29 | EXPECT_EQ(4, f(3)); 30 | 31 | const auto g = context.readVariable>("f"); 32 | EXPECT_EQ(4, g(3)); 33 | } 34 | 35 | TEST(FunctionsRead, LuaFunctions) { 36 | LuaContext context; 37 | 38 | context.executeCode("f = function(x) return x + 1; end"); 39 | 40 | const auto f = context.readVariable>("f"); 41 | EXPECT_EQ(4, f(3)); 42 | 43 | const auto g = context.readVariable>("f"); 44 | EXPECT_EQ(4, g(3)); 45 | } 46 | 47 | TEST(FunctionsRead, LuaFunctionsCleanup) { 48 | LuaContext context; 49 | 50 | context.executeCode("f = function(x) return x + 1; end"); 51 | const auto f = context.readVariable>("f"); 52 | context.writeVariable("f", nullptr); 53 | EXPECT_EQ(4, f(3)); 54 | } 55 | 56 | TEST(FunctionsRead, CallLuaFunctionFromWithinCallback) { 57 | LuaContext context; 58 | 59 | context.writeFunction("execute", [&](const std::string& varName) { 60 | const auto f = context.readVariable>(varName); 61 | EXPECT_EQ(5, f()); 62 | }); 63 | 64 | context.executeCode(R"( 65 | function test() 66 | return 5 67 | end 68 | 69 | execute("test") 70 | )"); 71 | } 72 | -------------------------------------------------------------------------------- /tests/functions_write.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(FunctionsWrite, NativeFunctions) { 5 | struct Foo { 6 | static int increment(int x) 7 | { 8 | return x + 1; 9 | } 10 | }; 11 | 12 | LuaContext context; 13 | 14 | context.writeVariable("f", &Foo::increment); 15 | context.writeFunction("g", &Foo::increment); 16 | context.writeFunction("h", &Foo::increment); 17 | 18 | EXPECT_EQ(3, context.executeCode("return f(2)")); 19 | EXPECT_EQ(13, context.executeCode("return g(12)")); 20 | EXPECT_EQ(9, context.executeCode("return h(8)")); 21 | EXPECT_THROW(context.executeCode("return f(true)"), LuaContext::ExecutionErrorException); 22 | } 23 | 24 | TEST(FunctionsWrite, ConstRefParameters) 25 | { 26 | struct Foo { 27 | static int length(const std::string& x) 28 | { 29 | EXPECT_EQ("test", x); 30 | return x.length(); 31 | } 32 | }; 33 | 34 | LuaContext context; 35 | 36 | context.writeVariable("f", &Foo::length); 37 | EXPECT_EQ(4, context.executeCode("return f('test')")); 38 | } 39 | 40 | TEST(FunctionsWrite, VariantParameters) 41 | { 42 | LuaContext context; 43 | 44 | struct Foo {}; 45 | context.writeVariable("foo", Foo{}); 46 | context.writeFunction("f", [](const boost::variant& val) { return val.which(); }); 47 | 48 | EXPECT_EQ(1, context.executeCode("return f(foo)")); 49 | EXPECT_EQ(0, context.executeCode("return f(3)")); 50 | } 51 | 52 | TEST(FunctionsWrite, FunctionObjects) { 53 | struct Foo { 54 | int operator()(int x) { 55 | return x + 1; 56 | } 57 | 58 | double operator()(double) { 59 | EXPECT_TRUE(false); 60 | return 0; 61 | } 62 | }; 63 | 64 | 65 | LuaContext context; 66 | 67 | context.writeVariable("f", std::function(Foo{})); 68 | context.writeFunction("g", Foo{}); 69 | 70 | EXPECT_EQ(3, context.executeCode("return f(2)")); 71 | EXPECT_EQ(13, context.executeCode("return g(12)")); 72 | } 73 | 74 | TEST(FunctionsWrite, FunctionObjectsConst) { 75 | struct Foo { 76 | int operator()(int x) { 77 | return x + 1; 78 | } 79 | 80 | int operator()(int x) const { 81 | return x + 1; 82 | } 83 | }; 84 | 85 | 86 | LuaContext context; 87 | 88 | context.writeVariable("f", std::function(Foo{})); 89 | context.writeFunction("g", Foo{}); 90 | 91 | EXPECT_EQ(3, context.executeCode("return f(2)")); 92 | EXPECT_EQ(13, context.executeCode("return g(12)")); 93 | } 94 | 95 | TEST(FunctionsWrite, FunctionObjectsAutodetect) { 96 | struct Foo { 97 | int operator()(int x) { 98 | return x + 1; 99 | } 100 | }; 101 | 102 | 103 | LuaContext context; 104 | 105 | context.writeVariable("f", std::function(Foo{})); 106 | context.writeFunction("g", Foo{}); 107 | context.writeFunction("h", Foo{}); 108 | 109 | EXPECT_EQ(3, context.executeCode("return f(2)")); 110 | EXPECT_EQ(13, context.executeCode("return g(12)")); 111 | EXPECT_EQ(9, context.executeCode("return h(8)")); 112 | } 113 | 114 | TEST(FunctionsWrite, Lambdas) { 115 | LuaContext context; 116 | 117 | const auto konst = 1; 118 | const auto lambda = [&](int x) { return x + konst; }; 119 | context.writeVariable("f", std::function(lambda)); 120 | context.writeFunction("g", lambda); 121 | context.writeFunction("h", lambda); 122 | 123 | EXPECT_EQ(3, context.executeCode("return f(2)")); 124 | EXPECT_EQ(13, context.executeCode("return g(12)")); 125 | EXPECT_EQ(9, context.executeCode("return h(8)")); 126 | } 127 | 128 | TEST(FunctionsWrite, DestructorCalled) { 129 | struct Foo { 130 | int operator()(int x) { 131 | return x + 1; 132 | } 133 | 134 | std::shared_ptr dummy; 135 | }; 136 | 137 | auto foo = Foo{ std::make_shared() }; 138 | std::weak_ptr dummy = foo.dummy; 139 | 140 | auto context = std::make_shared(); 141 | context->writeFunction("f", foo); 142 | foo.dummy.reset(); 143 | 144 | EXPECT_FALSE(dummy.expired()); 145 | context.reset(); 146 | EXPECT_TRUE(dummy.expired()); 147 | } 148 | 149 | TEST(FunctionsWrite, ReturningMultipleValues) { 150 | LuaContext context; 151 | 152 | context.writeFunction("f", [](int x) { return std::make_tuple(x, x+1, "hello"); }); 153 | context.executeCode("a, b, c = f(2)"); 154 | 155 | EXPECT_EQ(2, context.readVariable("a")); 156 | EXPECT_EQ(3, context.readVariable("b")); 157 | EXPECT_EQ("hello", context.readVariable("c")); 158 | } 159 | 160 | TEST(FunctionsWrite, PolymorphicFunctions) { 161 | LuaContext context; 162 | 163 | context.writeFunction("f", 164 | [](boost::variant x) -> std::string 165 | { 166 | if (x.which() == 0) 167 | return "int"; 168 | else if (x.which() == 1) 169 | return "bool"; 170 | else 171 | return "string"; 172 | } 173 | ); 174 | 175 | EXPECT_EQ("int", context.executeCode("return f(2)")); 176 | EXPECT_EQ("bool", context.executeCode("return f(true)")); 177 | EXPECT_EQ("string", context.executeCode("return f('test')")); 178 | } 179 | 180 | TEST(FunctionsWrite, VariadicFunctions) { 181 | LuaContext context; 182 | 183 | context.writeFunction("f", 184 | [](int a, boost::optional b, boost::optional c) -> int { 185 | return c.is_initialized() ? 3 : (b.is_initialized() ? 2 : 1); 186 | } 187 | ); 188 | 189 | EXPECT_EQ(1, context.executeCode("return f(12)")); 190 | EXPECT_EQ(2, context.executeCode("return f(12, 24)")); 191 | EXPECT_THROW(context.executeCode("return f(12, 24, \"hello\")"), LuaContext::ExecutionErrorException); 192 | EXPECT_EQ(3, context.executeCode("return f(12, 24, 3.5)")); 193 | 194 | context.writeFunction("g", [](boost::optional a, boost::optional b, int c) -> int { return 3; }); 195 | EXPECT_EQ(3, context.executeCode("return g(10, 20, 30)")); 196 | EXPECT_THROW(context.executeCode("return g(12, 24)"), LuaContext::ExecutionErrorException); 197 | } 198 | 199 | TEST(FunctionsWrite, AccessLuaFromWithinCallback) { 200 | LuaContext context; 201 | 202 | context.writeFunction("read", [&](const std::string& varName) { 203 | EXPECT_EQ(5, context.readVariable(varName)); 204 | }); 205 | 206 | context.executeCode("x = 5; read(\"x\");"); 207 | } 208 | 209 | TEST(FunctionsWrite, ExecuteLuaFromWithinCallback) { 210 | LuaContext context; 211 | 212 | context.writeFunction("exec", [&](const std::string& varName) { 213 | EXPECT_EQ("x", varName); 214 | context.executeCode("x = 10"); 215 | EXPECT_EQ(10, context.readVariable(varName)); 216 | }); 217 | 218 | context.executeCode("exec(\"x\")"); 219 | } 220 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } 7 | -------------------------------------------------------------------------------- /tests/metatables.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(Metatables, WritingMetatable) 5 | { 6 | LuaContext context; 7 | 8 | bool called = false; 9 | 10 | context.writeVariable("foo", LuaContext::EmptyArray); 11 | context.writeFunction("foo", LuaContext::Metatable, "__call", [&](const std::vector>&) { called = true; }); 12 | EXPECT_EQ("table", context.executeCode("return type(getmetatable(foo))")); 13 | 14 | context.executeCode("foo()"); 15 | EXPECT_TRUE(called); 16 | } 17 | 18 | TEST(Metatables, ReadingMetatable) 19 | { 20 | LuaContext context; 21 | 22 | context.writeVariable("foo", LuaContext::EmptyArray); 23 | context.writeVariable("foo", LuaContext::Metatable, "x", 18); 24 | 25 | EXPECT_EQ(18, context.executeCode("return getmetatable(foo).x")); 26 | EXPECT_EQ(18, context.readVariable("foo", LuaMetatable, "x")); 27 | } 28 | 29 | TEST(Metatables, WritingMetatableObjects) 30 | { 31 | struct Foo { int value = 0; }; 32 | 33 | LuaContext context; 34 | 35 | context.writeVariable("foo", Foo{}); 36 | context.writeFunction("foo", LuaContext::Metatable, "__call", [](Foo& foo) { foo.value++; }); 37 | context.writeFunction("foo", LuaContext::Metatable, "__index", [](Foo& foo, std::string index) -> int { foo.value += index.length(); return 12; }); 38 | 39 | context.executeCode("foo()"); 40 | EXPECT_EQ(12, context.executeCode("return foo.test")); 41 | 42 | EXPECT_EQ(5, context.readVariable("foo").value); 43 | } 44 | -------------------------------------------------------------------------------- /tests/movable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(Movable, PreserveValues) { 5 | LuaContext context1; 6 | context1.writeVariable("a", "hello"); 7 | 8 | LuaContext context2 = std::move(context1); 9 | EXPECT_EQ("hello", context2.readVariable("a")); 10 | } 11 | 12 | TEST(Movable, PreserveRegisteredFunctions) { 13 | struct Foo { 14 | int foo() { return 3; } 15 | }; 16 | 17 | LuaContext context1; 18 | context1.registerFunction("foo", &Foo::foo); 19 | context1.writeVariable("a", Foo{}); 20 | 21 | LuaContext context2 = std::move(context1); 22 | EXPECT_EQ(3, context2.executeCode("return a:foo()")); 23 | } 24 | 25 | TEST(Movable, PreserveReadFunctions) { 26 | LuaContext context1; 27 | context1.executeCode("f = function(i) return i + 1; end"); 28 | auto f = context1.readVariable>("f"); 29 | 30 | LuaContext context2 = std::move(context1); 31 | EXPECT_EQ(3, f(2)); 32 | 33 | f = {}; 34 | } 35 | -------------------------------------------------------------------------------- /tests/threads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(Threads, Basics) { 5 | LuaContext context; 6 | context.writeVariable("a", "hello"); 7 | 8 | auto thread1 = context.createThread(); 9 | auto thread2 = context.createThread(); 10 | 11 | context.executeCode(thread1, "a = 3"); 12 | EXPECT_EQ(3, context.readVariable("a")); 13 | EXPECT_EQ(3, context.readVariable(thread2, "a")); 14 | 15 | context.executeCode(thread2, "a = 18"); 16 | EXPECT_EQ(18, context.readVariable("a")); 17 | EXPECT_EQ(18, context.readVariable(thread1, "a")); 18 | 19 | context.writeVariable("a", "hello"); 20 | EXPECT_EQ("hello", context.readVariable(thread1, "a")); 21 | EXPECT_EQ("hello", context.readVariable(thread2, "a")); 22 | } 23 | --------------------------------------------------------------------------------