├── examples ├── demo │ ├── .gitignore │ ├── MyVector3d.h │ ├── MyActor.h │ ├── MyActorBinding.h │ ├── demo.cpp │ └── MyVector3dBinding.h ├── luaref │ ├── .gitignore │ └── luaref.cpp ├── tables │ ├── .gitignore │ ├── MyActor.h │ ├── MyActorBinding.h │ └── tables.cpp ├── upcast │ ├── .gitignore │ └── upcast.cpp ├── downcast │ ├── .gitignore │ └── downcast.cpp ├── guiexample │ ├── .gitignore │ ├── guiexample.cpp │ └── gui.lua ├── common │ ├── .gitignore │ ├── common.h │ └── common.cpp └── CMakeLists.txt ├── docs ├── _config.yml ├── TODO.md ├── Lua5.4.md ├── diagram.txt ├── README.md └── quick tutorial.md ├── tests ├── .gitignore ├── README ├── main.cpp ├── CMakeLists.txt ├── test_basic_pod.cpp ├── test_calling.cpp ├── test_properties.cpp ├── test_extras.cpp ├── test_luaref.cpp └── test_shared_pointer.cpp ├── .gitmodules ├── .gitignore ├── CMakeLists.txt ├── src ├── n4502.h ├── LuaException.h ├── LuaBinding.cpp ├── LuaRef.h ├── LuaStack.h └── LuaBinding.h ├── LICENSE ├── CMakeSettings.json └── compile_commands.json /examples/demo/.gitignore: -------------------------------------------------------------------------------- 1 | demo 2 | -------------------------------------------------------------------------------- /examples/luaref/.gitignore: -------------------------------------------------------------------------------- 1 | luaref 2 | -------------------------------------------------------------------------------- /examples/tables/.gitignore: -------------------------------------------------------------------------------- 1 | tables 2 | -------------------------------------------------------------------------------- /examples/upcast/.gitignore: -------------------------------------------------------------------------------- 1 | upcast 2 | -------------------------------------------------------------------------------- /examples/downcast/.gitignore: -------------------------------------------------------------------------------- 1 | downcast 2 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /examples/guiexample/.gitignore: -------------------------------------------------------------------------------- 1 | guiexample 2 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | test 3 | *.swp 4 | *~ 5 | -------------------------------------------------------------------------------- /examples/common/.gitignore: -------------------------------------------------------------------------------- 1 | common.o 2 | LuaBinding.o 3 | -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | See https://github.com/philsquared/Catch for details. 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/Catch2"] 2 | path = tests/Catch2 3 | url = https://github.com/catchorg/Catch2.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.dSYM 4 | .ycm_extra_conf.py 5 | build 6 | xcode 7 | .vs 8 | .vscode 9 | .cache 10 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | [Diagram](diagram.txt) | [Read Me](/manualbind) 2 | 3 | TODO List 4 | ========= 5 | 6 | Nothing here right now. 7 | -------------------------------------------------------------------------------- /examples/common/common.h: -------------------------------------------------------------------------------- 1 | // Common stuff for the examples. 2 | #ifndef COMMON_H 3 | #define COMMON_H 4 | 5 | #include 6 | 7 | void dump( lua_State* L ); 8 | void run( lua_State* L, const char* code ); 9 | 10 | #endif // COMMON_H 11 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void run( lua_State* L, const char* code ) 5 | { 6 | if( luaL_dostring( L, code ) ) 7 | { 8 | WARN( lua_tostring( L, -1 ) ); 9 | lua_pop( L, 1 ); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.0...3.31 ) 2 | project( ManualBind ) 3 | 4 | set( CMAKE_EXPORT_COMPILE_COMMANDS ON ) 5 | 6 | enable_testing() 7 | 8 | add_subdirectory( examples ) 9 | add_subdirectory( tests ) 10 | 11 | message( STATUS "Lua Library: ${LUA_LIBRARY}" ) 12 | -------------------------------------------------------------------------------- /examples/tables/MyActor.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYACTOR_H 2 | #define __MYACTOR_H 3 | #include 4 | 5 | class MyActor { 6 | 7 | public: 8 | 9 | std::string _name; 10 | 11 | MyActor( std::string name ) : _name(name) 12 | { 13 | std::cout << "Hello my name is " << _name << std::endl; 14 | } 15 | 16 | ~MyActor() 17 | { 18 | std::cout << "Goodbye from " << _name << std::endl; 19 | } 20 | }; 21 | 22 | #endif // __MYACTOR_H 23 | -------------------------------------------------------------------------------- /src/n4502.h: -------------------------------------------------------------------------------- 1 | #ifndef n4502_H 2 | #define n4502_H 3 | 4 | // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf. 5 | template 6 | using void_t = void; 7 | 8 | // Primary template handles all types not supporting the operation. 9 | template class, typename = void_t<> > 10 | struct detect : std::false_type {}; 11 | 12 | // Specialization recognizes/validates only types supporting the archetype. 13 | template class Op> 14 | struct detect > > : std::true_type {}; 15 | 16 | #endif // n4502_H 17 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Bulid tests 2 | cmake_minimum_required( VERSION 3.0...3.31 ) 3 | 4 | find_package( Lua ) 5 | 6 | add_subdirectory(Catch2) 7 | 8 | file( GLOB TEST_SRCS *.cpp ) 9 | 10 | include_directories( 11 | ../src 12 | ${LUA_INCLUDE_DIR} 13 | ) 14 | 15 | add_executable( runtests ${TEST_SRCS} ../src/LuaBinding.cpp ) 16 | 17 | target_link_libraries( runtests 18 | PRIVATE 19 | ${LUA_LIBRARY} 20 | ${LUA_MATH_LIBRARY} 21 | Catch2::Catch2WithMain 22 | ) 23 | 24 | add_test( tests runtests ) 25 | 26 | set_property( TARGET runtests PROPERTY CXX_STANDARD 20 ) 27 | set_property( TARGET runtests PROPERTY CXX_STANDARD_REQUIRED ON ) 28 | -------------------------------------------------------------------------------- /examples/demo/MyVector3d.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYVECTOR_H 2 | #define __MYVECTOR_H 3 | 4 | #include 5 | using std::cout; 6 | using std::endl; 7 | using std::hex; 8 | using std::dec; 9 | 10 | class MyVector3d 11 | { 12 | public: 13 | float x, y, z; 14 | 15 | MyVector3d( float nx, float ny, float nz ) : x(nx), y(ny), z(nz) 16 | { 17 | cout << "Hello from MyVector3d " << hex << this << dec << endl; 18 | } 19 | 20 | MyVector3d( const MyVector3d& vec ) : x(vec.x), y(vec.y), z(vec.z) 21 | { 22 | cout << "Hello from MyVector3d copy constructor " << hex << this << dec << endl; 23 | } 24 | 25 | ~MyVector3d() 26 | { 27 | cout << "Goodbye from MyVector3d " << hex << this << dec << endl; 28 | } 29 | }; 30 | 31 | #endif // __MYVECTOR_H 32 | -------------------------------------------------------------------------------- /examples/demo/MyActor.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYACTOR_H 2 | #define __MYACTOR_H 3 | #include 4 | #include 5 | 6 | class MyActor { 7 | protected: 8 | 9 | std::string _name; 10 | 11 | public: 12 | 13 | int _age; 14 | 15 | public: 16 | 17 | MyActor( std::string name, int age ) : _name(name), _age(age) 18 | { 19 | std::cout << "Hello my name is " << _name << " and I am " << _age << " years old." << std::endl; 20 | std::cout << std::hex << this << std::dec << std::endl; 21 | } 22 | 23 | void walk() { 24 | std::cout << _name << " is Walking\n"; 25 | } 26 | 27 | void setName( std::string name ) 28 | { 29 | std::cout << "Changing my name to: " << name << std::endl; 30 | _name = name; 31 | } 32 | 33 | ~MyActor() 34 | { 35 | std::cout << "Goodbye from " << _name << std::endl; 36 | std::cout << std::hex << this << std::dec << std::endl; 37 | } 38 | }; 39 | 40 | #endif // __MYACTOR_H 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | =========== 3 | 4 | Copyright (c) 2017 Nigel Atkinson 5 | --------------------------------- 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /examples/tables/MyActorBinding.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYACTORBINDING_H 2 | #define __MYACTORBINDING_H 3 | #include "LuaBinding.h" 4 | #include "MyActor.h" 5 | 6 | using MyActorPtr = std::shared_ptr; 7 | 8 | using namespace ManualBind; 9 | 10 | struct MyActorBinding: public Binding { 11 | 12 | static constexpr const char* class_name = "MyActor"; 13 | 14 | static bind_properties* properties() 15 | { 16 | static bind_properties properties[] = { 17 | { "name", get_name, nullptr }, 18 | { nullptr, nullptr, nullptr } 19 | }; 20 | return properties; 21 | } 22 | 23 | // Lua constructor 24 | static int create( lua_State *L ) 25 | { 26 | std::cout << "Create called\n"; 27 | 28 | CheckArgCount( L, 1 ); 29 | 30 | const char *name = luaL_checkstring( L, 1 ); 31 | 32 | MyActorPtr sp = std::make_shared( name ); 33 | 34 | push( L, sp ); 35 | 36 | return 1; 37 | } 38 | 39 | // Propertie getters and setters 40 | 41 | static int get_name( lua_State *L ) 42 | { 43 | CheckArgCount( L, 2 ); 44 | MyActorPtr a = fromStack( L, 1 ); 45 | lua_pushstring( L, a->_name.c_str() ); 46 | return 1; 47 | } 48 | 49 | }; 50 | 51 | #endif // __MYACTORBINDING_H 52 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0...3.31) 2 | 3 | find_package( Lua ) 4 | 5 | include_directories( 6 | ../src 7 | common 8 | ${LUA_INCLUDE_DIR} 9 | ) 10 | 11 | add_library( common common/common.cpp common/common.h ../src/LuaBinding.cpp ) 12 | 13 | set_property( TARGET common PROPERTY CXX_STANDARD 20 ) 14 | set_property( TARGET common PROPERTY CXX_STANDARD_REQUIRED ON ) 15 | 16 | function( example NAME DIR ) 17 | 18 | file( GLOB SRCS "${DIR}/*.cpp" "${DIR}/*.h" "${DIR}/*.lua" ) 19 | 20 | add_executable( ${NAME} ${SRCS} ) 21 | 22 | target_link_libraries( ${NAME} 23 | common 24 | ${LUA_LIBRARY} 25 | ${LUA_MATH_LIBRARY} 26 | ) 27 | 28 | set_property( TARGET ${NAME} PROPERTY CXX_STANDARD 20 ) 29 | set_property( TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON ) 30 | 31 | endfunction() 32 | 33 | example( exdemo demo ) 34 | example( exluaref luaref ) 35 | example( exdowncast downcast ) 36 | example( exupcast upcast ) 37 | example( extables tables ) 38 | example( exgui guiexample ) 39 | 40 | add_custom_command( 41 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gui.lua" 42 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/guiexample/gui.lua" "${CMAKE_CURRENT_BINARY_DIR}/gui.lua" 43 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/guiexample/gui.lua" 44 | ) 45 | add_custom_target( scriptcopy ALL DEPENDS "gui.lua" ) 46 | -------------------------------------------------------------------------------- /docs/Lua5.4.md: -------------------------------------------------------------------------------- 1 | Lua 5.4 2 | ======= 3 | 4 | Lua 5.4 allows you to annotate local varibles to either make them const, or const and closing. 5 | Closing varibles when they go out of scope will call thier __close metamethod. 6 | 7 | The binding now handles __close in addition to __gc, by resetting the shared pointer, effectively releasing that refcount for the object. This allows you to have more deterministic releasing of resources, without having to force garbage collections. 8 | 9 | Example 10 | ---------- 11 | Say you have a binding for a texure class which looks after allocated memory on the GPU in addition to normal ram. You want this to be cleaned up ASAP after use. 12 | 13 | ```Lua 14 | function setCaption(dialogbox, text) 15 | local texture = generateTextureFromText(text) 16 | dialogbox.setCaptionTexture(texture) 17 | end 18 | ``` 19 | 20 | Once this function finishes - normally without the annotation, the texture var becomes garbage and *eventually* it's __gc metamethod called which will destroy the shared pointer - reducing the ref count. 21 | 22 | However with the annotation, at the end of the function as 'texture' goes out of scope, it's __close metamethod will be called. This calls std::shared_ptr.reset() which will reduce the ref count. This happens as soon as the function finishes. Later, the __gc metamethod will destroy an empty (pointing at nullptr) shared pointer. 23 | -------------------------------------------------------------------------------- /tests/test_basic_pod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaBinding.h" 3 | 4 | using namespace ManualBind; 5 | 6 | class Basic 7 | { 8 | }; 9 | 10 | // A very basic opaque pointer binding. 11 | struct BasicPODbinding : public PODBinding 12 | { 13 | static constexpr const char* class_name = "Basic"; 14 | }; 15 | 16 | TEST_CASE( "Basic POD binding retains identical pointer value." ) { 17 | 18 | lua_State* L = luaL_newstate(); 19 | 20 | BasicPODbinding::register_class( L ); 21 | 22 | Basic* bp = new Basic(); 23 | 24 | // Give Lua a copy. 25 | BasicPODbinding::push( L, bp ); 26 | lua_setglobal( L, "bp" ); 27 | 28 | // Retrieve 29 | lua_getglobal( L, "bp" ); 30 | Basic* fromLua = BasicPODbinding::fromStackThrow( L, 1 ); 31 | lua_pop( L, 1 ); 32 | 33 | REQUIRE( bp == fromLua ); 34 | 35 | lua_close( L ); 36 | 37 | delete bp; 38 | } 39 | 40 | TEST_CASE( "Basic POD binding can test for type." ) { 41 | 42 | lua_State* L = luaL_newstate(); 43 | 44 | BasicPODbinding::register_class( L ); 45 | 46 | Basic* bp = new Basic(); 47 | 48 | // Give Lua a copy. 49 | BasicPODbinding::push( L, bp ); 50 | lua_setglobal( L, "bp" ); 51 | 52 | // Check the 'type' 53 | lua_getglobal( L, "bp" ); 54 | REQUIRE( BasicPODbinding::isType(L, -1) == true ); 55 | lua_pop( L, 1 ); 56 | 57 | // Check for false positive 58 | lua_pushinteger(L, 42); 59 | REQUIRE( BasicPODbinding::isType(L, -1) == false ); 60 | lua_pop( L, 1 ); 61 | 62 | lua_close( L ); 63 | 64 | delete bp; 65 | } 66 | -------------------------------------------------------------------------------- /tests/test_calling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaBinding.h" 3 | 4 | void run( lua_State* L, const char* code ); 5 | 6 | using namespace ManualBind; 7 | 8 | class Widget 9 | { 10 | public: 11 | std::string stringy; 12 | int numbery; 13 | 14 | void setThings( std::string str, int num ) 15 | { 16 | stringy = str; 17 | numbery = num; 18 | } 19 | }; 20 | 21 | using WidgetPtr = std::shared_ptr; 22 | 23 | struct WidgetBinding : public Binding 24 | { 25 | static constexpr const char* class_name = "Widget"; 26 | 27 | static luaL_Reg* members() 28 | { 29 | static luaL_Reg members[] = 30 | { 31 | { "setThings", callSetThings }, 32 | { nullptr, nullptr } 33 | }; 34 | return members; 35 | } 36 | 37 | static int callSetThings( lua_State* L ) 38 | { 39 | WidgetPtr p = fromStack( L, 1 ); 40 | 41 | std::string str( luaL_checkstring( L, 2 ) ); 42 | int num = luaL_checkinteger( L, 3 ); 43 | 44 | p->setThings( str, num ); 45 | 46 | return 1; 47 | } 48 | }; 49 | 50 | TEST_CASE( "Lua can call member functions." ) { 51 | 52 | lua_State* L = luaL_newstate(); 53 | 54 | WidgetBinding::register_class( L ); 55 | 56 | WidgetPtr p = std::make_shared(); 57 | 58 | WidgetBinding::push( L, p ); 59 | lua_setglobal( L, "widget" ); 60 | 61 | run( L, "widget:setThings( 'Hello World', 42 )" ); 62 | 63 | REQUIRE( p->stringy == "Hello World" ); 64 | 65 | REQUIRE( p->numbery == 42 ); 66 | 67 | lua_close( L ); 68 | } 69 | -------------------------------------------------------------------------------- /tests/test_properties.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaBinding.h" 3 | 4 | void run( lua_State* L, const char* code ); 5 | 6 | using namespace ManualBind; 7 | 8 | class Person 9 | { 10 | public: 11 | std::string name; 12 | }; 13 | 14 | using PersonPtr = std::shared_ptr; 15 | 16 | struct PersonBinding : public Binding 17 | { 18 | static constexpr const char* class_name = "Person"; 19 | 20 | static bind_properties* properties() 21 | { 22 | static bind_properties properties[] = 23 | { 24 | { "name", getName, setName }, 25 | { nullptr, nullptr, nullptr } 26 | }; 27 | return properties; 28 | } 29 | 30 | static int getName( lua_State* L ) 31 | { 32 | PersonPtr p = fromStack( L, 1 ); 33 | 34 | lua_pushstring( L, p->name.c_str() ); 35 | 36 | return 1; 37 | } 38 | 39 | static int setName( lua_State* L ) 40 | { 41 | PersonPtr p = fromStack( L, 1 ); 42 | 43 | // 2 is the property name 44 | 45 | const char* name = luaL_checkstring( L, 3 ); 46 | 47 | p->name = name; 48 | 49 | return 0; 50 | } 51 | 52 | }; 53 | 54 | TEST_CASE( "Lua can get and set properties." ) { 55 | 56 | lua_State* L = luaL_newstate(); 57 | 58 | PersonBinding::register_class( L ); 59 | 60 | PersonPtr pp = std::make_shared(); 61 | 62 | PersonBinding::push( L, pp ); 63 | lua_setglobal( L, "person" ); 64 | 65 | run( L, "person.name ='Bob'" ); 66 | 67 | REQUIRE( pp->name == "Bob" ); 68 | 69 | pp->name = "Jane"; 70 | 71 | run( L, "name = person.name" ); 72 | 73 | lua_getglobal( L, "name" ); 74 | std::string name (luaL_checkstring( L, -1 )); 75 | 76 | REQUIRE( name == "Jane" ); 77 | 78 | lua_close( L ); 79 | } 80 | -------------------------------------------------------------------------------- /tests/test_extras.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaBinding.h" 3 | 4 | void run( lua_State* L, const char* code ); 5 | 6 | using namespace ManualBind; 7 | 8 | class Extra 9 | { 10 | }; 11 | 12 | using ExtraPtr = std::shared_ptr; 13 | 14 | struct ExtraBinding : public Binding 15 | { 16 | static constexpr const char* class_name = "Extra"; 17 | 18 | static void setExtraMeta( lua_State* L ) 19 | { 20 | lua_pushliteral( L, "Alara" ); 21 | lua_setfield( L, -2, "DEF_NAME" ); 22 | } 23 | }; 24 | 25 | TEST_CASE( "Can set extra elements in class meta table." ) { 26 | 27 | lua_State* L = luaL_newstate(); 28 | 29 | ExtraBinding::register_class( L ); 30 | 31 | ExtraPtr pp = std::make_shared(); 32 | 33 | ExtraBinding::push( L, pp ); 34 | lua_setglobal( L, "person" ); 35 | 36 | run( L, "name = person.DEF_NAME" ); 37 | 38 | lua_getglobal( L, "name" ); 39 | std::string name( lua_tostring( L, -1 ) ); 40 | 41 | REQUIRE( name == "Alara" ); 42 | 43 | lua_close( L ); 44 | } 45 | 46 | TEST_CASE( "Lua can assign extra elements per instance." ) { 47 | 48 | lua_State* L = luaL_newstate(); 49 | 50 | ExtraBinding::register_class( L ); 51 | 52 | ExtraPtr p1 = std::make_shared(); 53 | ExtraBinding::push( L, p1 ); 54 | lua_setglobal( L, "p1" ); 55 | 56 | ExtraPtr p2 = std::make_shared(); 57 | ExtraBinding::push( L, p2 ); 58 | lua_setglobal( L, "p2" ); 59 | 60 | run( L, "p1.perinstance = 'Nigel'" ); 61 | 62 | run( L, "name = p1.perinstance" ); 63 | 64 | lua_getglobal( L, "name" ); 65 | std::string name( lua_tostring( L, -1 ) ); 66 | 67 | REQUIRE( name == "Nigel" ); 68 | 69 | run( L, "name = p2.perinstance" ); 70 | 71 | lua_getglobal( L, "name" ); 72 | 73 | REQUIRE( lua_type( L, -1 ) == LUA_TNIL ); 74 | 75 | lua_close( L ); 76 | }; 77 | -------------------------------------------------------------------------------- /tests/test_luaref.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaRef.h" 3 | 4 | void run( lua_State* L, const char* code ); 5 | 6 | using namespace ManualBind; 7 | 8 | TEST_CASE( "Can call lua functions with LuaRef" ) { 9 | 10 | lua_State* L = luaL_newstate(); 11 | 12 | run( L, "function set( nx, ny, nz ) x = nx y = ny z = nz end" ); 13 | 14 | { 15 | LuaRef set( L, "set" ); 16 | 17 | REQUIRE( set.isFunction() == true ); 18 | 19 | set( "Hello", 10, set ); 20 | 21 | LuaRef x( L, "x" ); 22 | LuaRef y( L, "y" ); 23 | LuaRef z( L, "z" ); 24 | 25 | REQUIRE( x.isString() == true ); 26 | REQUIRE( y.isNumber() == true ); 27 | REQUIRE( z.isFunction() == true ); 28 | 29 | } // Importaint: LuaRefs going out of scope and destructing, before lua_close(); 30 | 31 | lua_close( L ); 32 | } 33 | 34 | TEST_CASE( "LuaRef can index and access tables." ) { 35 | 36 | lua_State* L = luaL_newstate(); 37 | 38 | { 39 | LuaRef table = LuaRef::newTable( L ); 40 | table.push(); 41 | lua_setglobal( L, "t" ); 42 | 43 | run( L, "for i = 1, 100 do t[i] = i end" ); 44 | 45 | REQUIRE( (int)table[25] == 25 ); 46 | REQUIRE( (int)table[1] == 1 ); 47 | REQUIRE( (int)table[100] == 100 ); 48 | REQUIRE( table[1000].isNil() == true ); 49 | } 50 | 51 | lua_close( L ); 52 | } 53 | 54 | TEST_CASE( "Accessing table elements leaves luaTop at the same place." ) { 55 | 56 | lua_State* L = luaL_newstate(); 57 | 58 | { 59 | LuaRef table = LuaRef::newTable( L ); 60 | table.push(); 61 | lua_setglobal( L, "t"); 62 | 63 | run( L, "t[5] = 123"); 64 | 65 | int top = lua_gettop( L ); 66 | 67 | int number = table[5]; 68 | 69 | int topAfter = lua_gettop( L ); 70 | 71 | REQUIRE(number == 123); 72 | REQUIRE(top == topAfter); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/demo/MyActorBinding.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYACTORBINDING_H 2 | #define __MYACTORBINDING_H 3 | #include "LuaBinding.h" 4 | #include "MyActor.h" 5 | 6 | using MyActorPtr = std::shared_ptr; 7 | 8 | using namespace ManualBind; 9 | 10 | struct MyActorBinding: public Binding { 11 | 12 | static constexpr const char* class_name = "MyActor"; 13 | 14 | static luaL_Reg* members() 15 | { 16 | static luaL_Reg members[] = { 17 | { "walk", walk }, 18 | { "setName", setName }, 19 | { nullptr, nullptr } 20 | }; 21 | return members; 22 | } 23 | 24 | static bind_properties* properties() { 25 | static bind_properties properties[] = { 26 | { "age", get_age, set_age }, 27 | { nullptr, nullptr, nullptr } 28 | }; 29 | return properties; 30 | } 31 | 32 | // Lua constructor 33 | static int create( lua_State *L ) 34 | { 35 | std::cout << "Create called\n"; 36 | 37 | CheckArgCount( L, 2 ); 38 | 39 | const char *name = luaL_checkstring( L, 1 ); 40 | int age = luaL_checkinteger( L, 2 ); 41 | 42 | MyActorPtr sp = std::make_shared( name, age ); 43 | 44 | push( L, sp ); 45 | 46 | return 1; 47 | } 48 | 49 | // Method glue functions 50 | // 51 | 52 | static int walk( lua_State *L ) 53 | { 54 | CheckArgCount( L, 1 ); 55 | 56 | MyActorPtr a = fromStack( L, 1 ); 57 | 58 | a->walk(); 59 | 60 | return 0; 61 | } 62 | 63 | static int setName( lua_State *L ) 64 | { 65 | CheckArgCount( L, 2 ); 66 | 67 | MyActorPtr a = fromStack( L, 1 ); 68 | const char *name = lua_tostring( L, 2 ); 69 | 70 | a->setName( name ); 71 | 72 | return 0; 73 | } 74 | 75 | // Propertie getters and setters 76 | 77 | // 1 - class metatable 78 | // 2 - key 79 | static int get_age( lua_State *L ) 80 | { 81 | CheckArgCount( L, 2 ); 82 | 83 | MyActorPtr a = fromStack( L, 1 ); 84 | 85 | lua_pushinteger( L, a->_age ); 86 | 87 | return 1; 88 | } 89 | 90 | // 1 - class metatable 91 | // 2 - key 92 | // 3 - value 93 | static int set_age( lua_State *L ) 94 | { 95 | CheckArgCount( L, 3 ); 96 | 97 | MyActorPtr a = fromStack( L, 1 ); 98 | 99 | int age = luaL_checkinteger( L, 3 ); 100 | 101 | a->_age = age; 102 | 103 | return 0; 104 | } 105 | 106 | }; 107 | 108 | #endif // __MYACTORBINDING_H 109 | -------------------------------------------------------------------------------- /examples/tables/tables.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "LuaBinding.h" 3 | #include "LuaRef.h" 4 | #include 5 | #include 6 | #include "MyActor.h" 7 | #include "MyActorBinding.h" 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | using MyActorList = std::vector; 13 | 14 | MyActorList createList() 15 | { 16 | MyActorList actors { 17 | std::make_shared( "James" ), 18 | std::make_shared( "Who? Random extra" ), 19 | std::make_shared( "Harry" ), 20 | std::make_shared( "Mike" ) 21 | }; 22 | 23 | return actors; 24 | } 25 | 26 | void pushToLua( lua_State* L, MyActorList list ) 27 | { 28 | lua_newtable( L ); 29 | int i = 1; 30 | 31 | for( const auto& actor : list ) { 32 | MyActorBinding::push( L, actor ); 33 | lua_rawseti( L, -2, i++); 34 | } 35 | } 36 | 37 | MyActorList pullFromLua( lua_State* L ) 38 | { 39 | // Note this only stores the values, not the keys/indexes, 40 | // which are usually just numbers. 41 | // Also it simply skips over any elements that are not 'MyActor's. 42 | MyActorList list; 43 | 44 | if( lua_istable( L, -1 ) ) 45 | { 46 | lua_pushnil( L ); 47 | 48 | while( lua_next( L, -2 ) ) 49 | { 50 | if( luaL_testudata( L, -1, "MyActor" ) ) 51 | { 52 | list.push_back( MyActorBinding::fromStack( L, -1 ) ); 53 | } 54 | lua_pop( L, 1 ); 55 | } 56 | } 57 | 58 | return list; 59 | } 60 | 61 | int main() 62 | { 63 | lua_State* L = luaL_newstate(); 64 | luaL_openlibs( L ); 65 | 66 | MyActorBinding::register_class( L ); 67 | 68 | { 69 | cout << "Pushing actor list to Lua." << endl; 70 | MyActorList actors = createList(); 71 | pushToLua( L, actors ); 72 | lua_setglobal( L, "actors" ); 73 | } 74 | 75 | run( L, "for k,v in pairs(actors) do print( k, v.name ) end" ); 76 | 77 | cout << "Editing actor list..." << endl; 78 | run( L, "actors[2] = nil -- Who is this? Pfft, delete." ); 79 | run( L, "collectgarbage()" ); 80 | run( L, "actors['test'] = MyActor('Bob') -- Hey Bob welcome." ); 81 | 82 | run( L, "for k,v in pairs(actors) do print( k, v.name ) end" ); 83 | 84 | { 85 | cout << "Pull list back into C++ vector, and list..." << endl; 86 | lua_getglobal( L, "actors" ); 87 | MyActorList actors = pullFromLua( L ); 88 | lua_pop( L, 1 ); 89 | 90 | for( const auto& actor : actors ) 91 | { 92 | cout << actor->_name << endl; 93 | } 94 | } 95 | 96 | lua_close(L); 97 | } 98 | -------------------------------------------------------------------------------- /examples/common/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | 4 | using std::cout; 5 | using std::endl; 6 | 7 | void dump( lua_State* L ) 8 | { 9 | int i=lua_gettop(L); 10 | cout << " ---------------- Stack Dump ----------------\n"; 11 | while( i ) { 12 | int t = lua_type(L, i); 13 | switch (t) { 14 | case LUA_TSTRING: 15 | printf("%d: “%s”\n", i, lua_tostring(L, i)); 16 | break; 17 | case LUA_TBOOLEAN: 18 | printf("%d: %s\n",i,lua_toboolean(L, i) ? "true" : "false"); 19 | break; 20 | case LUA_TNUMBER: 21 | printf("%d: %g\n", i, lua_tonumber(L, i)); 22 | break; 23 | default: 24 | printf("%d: %s 0x%lX\n", i, lua_typename(L, t), (unsigned long)lua_topointer(L, i)); 25 | break; 26 | } 27 | i--; 28 | } 29 | cout << "--------------- Stack Dump Finished ---------------\n"; 30 | } 31 | 32 | void run( lua_State* L, const char* code ) 33 | { 34 | cout << "code: " << code << endl; 35 | if( luaL_dostring( L, code ) ) 36 | { 37 | cout << lua_tostring( L, -1 ) << endl; 38 | lua_pop( L, 1 ); 39 | } 40 | } 41 | 42 | void printtable( lua_State* L, int index ) 43 | { 44 | int t; 45 | if( lua_type( L, index ) != LUA_TTABLE ) { 46 | printf( "Index: %d is not a table.", index ); 47 | return; 48 | } 49 | 50 | lua_pushnil( L ); 51 | 52 | printf( "Key - Value\n" ); 53 | while( lua_next( L, index ) != 0 ) { 54 | t = lua_type(L, -2); 55 | switch (t) { 56 | case LUA_TSTRING: 57 | printf("“%s” - ", lua_tostring(L, -2)); 58 | break; 59 | case LUA_TBOOLEAN: 60 | printf("%s - ", lua_toboolean(L, -2) ? "true" : "false"); 61 | break; 62 | case LUA_TNUMBER: 63 | printf("%g - ", lua_tonumber(L, -2)); 64 | break; 65 | default: 66 | printf("%s 0x%lX - ", lua_typename(L, t), (unsigned long)lua_topointer(L, -2 )); 67 | break; 68 | } 69 | t = lua_type(L, -1); 70 | switch (t) { 71 | case LUA_TSTRING: 72 | printf("“%s”\n", lua_tostring(L, -1)); 73 | break; 74 | case LUA_TBOOLEAN: 75 | printf("%s\n", lua_toboolean(L, -1) ? "true" : "false"); 76 | break; 77 | case LUA_TNUMBER: 78 | printf("%g\n", lua_tonumber(L, -1)); 79 | break; 80 | default: 81 | printf("%s 0x%lX\n", lua_typename(L, t), (unsigned long)lua_topointer(L, -1 )); 82 | break; 83 | } 84 | lua_pop( L, 1 ); 85 | } 86 | 87 | return; 88 | } 89 | -------------------------------------------------------------------------------- /docs/diagram.txt: -------------------------------------------------------------------------------- 1 | Lua Tables and objects used in Bindings. 2 | ───────────────────────────────────────── 3 | 4 | A table stored globally as "ClassName" represents each class. This table 5 | has a __call metametod C function that creates a class instance. 6 | This table also provides a __pairs metamethod for class introspection 7 | and the __index field is set to the metatable. 8 | 9 | 10 | A class instance looks like the following: 11 | 12 | ┌─USERDATA────────────────┐ ┌─TABLE────────────────────┐ 13 | │ Class userdata. │ │ Class metatable. │ 14 | ├─────────────────────────┤ ├──────────────────────────┤ 15 | │ Represents an instance │ │ │ 16 | │ │ │ │ 17 | │ │ │ __gc ├──> C function to destroy 18 | │ │ │ │ or clean up class 19 | │ │ │ │ 20 | │ shared meta table ├─Metatable─>│ __upcast ├──> Optional C function to 21 | │ │ │ │ cast class up to a base 22 | │ │ │ │ class 23 | │ │ │ __properties ├──┐ 24 | │ │ │ │ │ 25 | └─────────────────────────┘ │ user def members... │ │ 26 | │ │ │ 27 | └──────────────────────────┘ │ 28 | ┌─TABLE───────────────────┐ │ 29 | │ Table of properties │<─────────────────────────────────────────┘ 30 | ├─────────────────────────┤ 31 | │ │ ┌─TABLE──────┐ 32 | │ Each entry is a table ├────>│ get ├──> C function to get the prop 33 | └─────────────────────────┘ │ set ├──> Optional C function to set the prop 34 | └────────────┘ 35 | 36 | LUA_REGISTRY ───┐ 37 | │ 38 | ┌─TABLE───────────────────────┐ ┌─TABLE────────────────────────┐ 39 | │ Per instance extra elements │ │ extra elements │ 40 | ├─────────────────────────────┤ ├──────────────────────────────┤ 41 | │ class userdata ├──────>│ Created on first use. │ 42 | │ │ └──────────────────────────────┘ 43 | │ created on first use │ 44 | └─────────────────────────────┘ 45 | 46 | Depending on the binding type, the userdata memory will contain either a 47 | shared pointer to the C++ class instance, or the value itself (POD). 48 | -------------------------------------------------------------------------------- /tests/test_shared_pointer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "LuaBinding.h" 3 | 4 | void run( lua_State* L, const char* code ); 5 | 6 | using namespace ManualBind; 7 | 8 | class Basic 9 | { 10 | }; 11 | 12 | struct BasicBinding : public Binding 13 | { 14 | static constexpr const char* class_name = "Basic"; 15 | }; 16 | 17 | using BasicPtr = std::shared_ptr; 18 | 19 | TEST_CASE( "Binding maintains shared pointer to same class instance." ) { 20 | 21 | lua_State* L = luaL_newstate(); 22 | 23 | BasicBinding::register_class( L ); 24 | 25 | BasicPtr bp = std::make_shared(); 26 | 27 | // Give 28 | BasicBinding::push( L, bp ); 29 | lua_setglobal( L, "bp" ); 30 | 31 | // Retrieve 32 | lua_getglobal( L, "bp" ); 33 | BasicPtr fromLua = BasicBinding::fromStackThrow( L, 1 ); 34 | lua_pop( L, 1 ); 35 | 36 | REQUIRE( bp == fromLua ); 37 | 38 | lua_close( L ); 39 | } 40 | 41 | TEST_CASE( "Garbage collection calls shared pointer destructor." ) { 42 | 43 | lua_State* L = luaL_newstate(); 44 | 45 | BasicBinding::register_class( L ); 46 | 47 | BasicPtr bp = std::make_shared(); 48 | 49 | REQUIRE( bp.use_count() == 1 ); 50 | 51 | BasicBinding::push( L, bp ); 52 | lua_setglobal( L, "bp" ); 53 | 54 | REQUIRE( bp.use_count() == 2 ); 55 | 56 | // Garbage collect Lua's pointer. 57 | lua_pushnil( L ); 58 | lua_setglobal( L, "bp" ); 59 | lua_gc( L, LUA_GCCOLLECT, 0 ); 60 | 61 | REQUIRE( bp.use_count() == 1 ); 62 | 63 | lua_close( L ); 64 | } 65 | 66 | TEST_CASE( "Closing Lua state calls shared pointer destructor." ) { 67 | 68 | lua_State* L = luaL_newstate(); 69 | 70 | BasicBinding::register_class( L ); 71 | 72 | BasicPtr bp = std::make_shared(); 73 | 74 | REQUIRE( bp.use_count() == 1 ); 75 | 76 | BasicBinding::push( L, bp ); 77 | lua_setglobal( L, "bp" ); 78 | 79 | REQUIRE( bp.use_count() == 2 ); 80 | 81 | lua_close( L ); 82 | 83 | REQUIRE( bp.use_count() == 1 ); 84 | } 85 | 86 | TEST_CASE( "Lua object going out of scope with close annotation resets shared pointer." ) { 87 | 88 | lua_State* L = luaL_newstate(); 89 | luaL_openlibs(L); 90 | 91 | BasicBinding::register_class( L ); 92 | 93 | run(L, "function scope(bp) local lbp = bp coroutine.yield() end"); 94 | run(L, "co = coroutine.wrap(scope)"); 95 | 96 | BasicPtr bp = std::make_shared(); 97 | 98 | REQUIRE( bp.use_count() == 1 ); 99 | 100 | int nResults, ret; 101 | lua_getglobal( L, "co" ); 102 | BasicBinding::push( L, bp ); 103 | ret = lua_resume( L, nullptr, 1, &nResults); 104 | 105 | REQUIRE( bp.use_count() == 2 ); 106 | 107 | lua_getglobal( L, "co" ); 108 | ret = lua_resume( L, nullptr, 0, &nResults); 109 | 110 | REQUIRE( bp.use_count() == 1 ); 111 | 112 | lua_close( L ); 113 | } 114 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. 3 | "environments": [ 4 | { 5 | "LuaDir": "${env.USERPROFILE}\\Documents\\lua-5.3.4" 6 | } 7 | ], 8 | "configurations": [ 9 | { 10 | "name": "x86-Debug", 11 | "generator": "Ninja", 12 | "configurationType": "Debug", 13 | "inheritEnvironments": [ "msvc_x86" ], 14 | "buildRoot": "${projectDir}\\build\\${name}", 15 | "installRoot": "${projectDir}\\install\\${name}", 16 | "cmakeCommandArgs": "", 17 | "buildCommandArgs": "-v", 18 | "ctestCommandArgs": "", 19 | "variables": [ 20 | { 21 | "name": "LUA_INCLUDE_DIR", 22 | "value": "${env.LuaDir}\\src" 23 | }, 24 | { 25 | "name": "LUA_LIBRARY", 26 | "value": "${env.LuaDir}\\build\\${name}\\lua.lib" 27 | } 28 | ] 29 | }, 30 | { 31 | "name": "x86-Release", 32 | "generator": "Ninja", 33 | "configurationType": "RelWithDebInfo", 34 | "inheritEnvironments": [ "msvc_x86" ], 35 | "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", 36 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 37 | "cmakeCommandArgs": "", 38 | "buildCommandArgs": "-v", 39 | "ctestCommandArgs": "", 40 | "variables": [ 41 | { 42 | "name": "LUA_INCLUDE_DIR", 43 | "value": "${env.LuaDir}\\src" 44 | }, 45 | { 46 | "name": "LUA_LIBRARY", 47 | "value": "${env.LuaDir}\\build\\${name}\\lua.lib" 48 | } 49 | ] 50 | }, 51 | { 52 | "name": "x64-Debug", 53 | "generator": "Ninja", 54 | "configurationType": "Debug", 55 | "inheritEnvironments": [ "msvc_x64_x64" ], 56 | "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", 57 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 58 | "cmakeCommandArgs": "", 59 | "buildCommandArgs": "-v", 60 | "ctestCommandArgs": "", 61 | "variables": [ 62 | { 63 | "name": "LUA_INCLUDE_DIR", 64 | "value": "${env.LuaDir}\\src" 65 | }, 66 | { 67 | "name": "LUA_LIBRARY", 68 | "value": "${env.LuaDir}\\build\\${name}\\lua.lib" 69 | } 70 | ] 71 | }, 72 | { 73 | "name": "x64-Release", 74 | "generator": "Ninja", 75 | "configurationType": "RelWithDebInfo", 76 | "inheritEnvironments": [ "msvc_x64_x64" ], 77 | "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", 78 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 79 | "cmakeCommandArgs": "", 80 | "buildCommandArgs": "-v", 81 | "ctestCommandArgs": "", 82 | "variables": [ 83 | { 84 | "name": "LUA_INCLUDE_DIR", 85 | "value": "${env.LuaDir}\\src" 86 | }, 87 | { 88 | "name": "LUA_LIBRARY", 89 | "value": "${env.LuaDir}\\build\\${name}\\lua.lib" 90 | } 91 | ] 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /examples/demo/demo.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include 4 | #include "MyActor.h" 5 | #include "MyActorBinding.h" 6 | #include "MyVector3d.h" 7 | #include "MyVector3dBinding.h" 8 | #include "LuaRef.h" 9 | 10 | using std::cout; 11 | using std::endl; 12 | 13 | int main(int argc, char **argv ) 14 | { 15 | lua_State* L = luaL_newstate(); 16 | luaL_openlibs( L ); 17 | run( L, "print( 'Selam Dünya' ) print( _VERSION )" ); 18 | 19 | MyActorBinding::register_class( L ); 20 | MyVector3dBinding::register_class( L ); 21 | 22 | run( L, "a = MyActor( 'Alara', 27 )" ); 23 | run( L, "a:walk()" ); 24 | run( L, "b = a" ); 25 | run( L, "a:setName('Awesome Chick')" ); 26 | run( L, "a.extra = 'Coffee!'" ); 27 | 28 | { 29 | lua_getglobal( L, "b" ); 30 | MyActorPtr b = MyActorBinding::fromStackThrow( L, -1 ); 31 | lua_pop( L, 1 ); 32 | 33 | cout << "Use count is now: " << b.use_count() << endl; 34 | b->walk(); 35 | } 36 | 37 | { 38 | MyActorPtr actor = std::make_shared("Nigel",39); 39 | cout << "Actor use count is: " << actor.use_count() << endl; 40 | MyActorBinding::push( L, actor ); 41 | lua_setglobal( L, "actor" ); 42 | cout << "Pushed to Lua" << endl; 43 | cout << "Actor use count is: " << actor.use_count() << endl; 44 | run( L, "actor:walk()" ); 45 | run( L, "actor.age = actor.age + 1 print( 'Happy Birthday')" ); 46 | run( L, "print( actor.age )" ); 47 | cout << actor->_age << endl; 48 | // Should print Coffee, nil as 'added' members/properties are per instance. 49 | run( L, "print( a.extra, actor.extra )" ); 50 | } 51 | 52 | { 53 | lua_getglobal( L, "a" ); 54 | LuaBindingGetExtraValuesTable( L, -1 ); 55 | LuaRef extras = LuaRef::fromStack( L, -1 ); 56 | lua_pop( L, 2 ); // "a", and the table. 57 | cout << extras["extra"].tostring() << endl; 58 | } 59 | 60 | run( L, "b = MyActor( 'Short lived', 0 )" ); 61 | run( L, "b = nil" ); 62 | run( L, "collectgarbage()" ); 63 | 64 | { 65 | MyActorPtr shorty = std::make_shared("Shorty", 5); 66 | cout << "Shorty use count is: " << shorty.use_count() << endl; 67 | MyActorBinding::push( L, shorty ); 68 | lua_setglobal( L, "shorty" ); 69 | cout << "Shorty use count is: " << shorty.use_count() << endl; 70 | lua_pushnil( L ); 71 | lua_setglobal( L, "shorty" ); 72 | run( L, "collectgarbage()" ); 73 | cout << "Shorty use count is: " << shorty.use_count() << endl; 74 | } 75 | 76 | // Override (for all instances) a method, while calling the old implementation 77 | // This updates the metatable because 'walk' is already defined there. 78 | run( L, "local old = actor.walk actor.walk = function(self) old(self) print( 'RUN!' ) end" ); 79 | run( L, "actor:walk()" ); 80 | 81 | run( L, "a:splat()" ); 82 | 83 | run( L, "v = MyVector3d( 1, 2, 3 )" ); 84 | run( L, "v.x = 4" ); 85 | run( L, "print( v.x, v.y, v.z )" ); 86 | run( L, "print( MyVector3d.ZERO )" ); 87 | 88 | cout << "Closing Lua\n"; 89 | lua_close( L ); 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /examples/guiexample/guiexample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "LuaBinding.h" 7 | 8 | using std::cout; 9 | using std::endl; 10 | 11 | // Some GUI class that represents a 2D rectangular region. 12 | // This could be a 'window' or a 'button', etc. 13 | // 14 | // ( Like Betajean's Gorilla's rectangle. ) 15 | // 16 | 17 | class Rectangle 18 | { 19 | int _x, _y, _width, _height; 20 | 21 | public: 22 | Rectangle( int x, int y, int w, int h ) : _x(x), _y(y), _width(w), _height(h) 23 | { 24 | cout << "Created Rectangle" << endl; 25 | } 26 | 27 | bool isInside( int tx, int ty ) 28 | { 29 | cout << "Checking x: " << tx << ", y: " << ty; 30 | cout << " Against ( " << _x << ", " << _y << " ) - ( " << _x + _width << ", " << _y + _height << " )" << endl; 31 | if( tx < _x ) 32 | return false; 33 | if( ty < _y ) 34 | return false; 35 | if( tx > _x + _width ) 36 | return false; 37 | if( ty > _y + _height ) 38 | return false; 39 | 40 | return true; 41 | } 42 | virtual ~Rectangle() 43 | { 44 | cout << "Destroyed Rectangle" << endl; 45 | } 46 | }; 47 | 48 | using RectanglePtr = std::shared_ptr; 49 | 50 | using namespace ManualBind; 51 | 52 | struct RectangleBinding: public Binding 53 | { 54 | 55 | static constexpr const char* class_name = "GUIRectangle"; 56 | 57 | static luaL_Reg* members() 58 | { 59 | static luaL_Reg members[] = { 60 | { "isInside", isInside }, 61 | { nullptr, nullptr } 62 | }; 63 | return members; 64 | } 65 | 66 | // Lua constructor 67 | static int create( lua_State *L ) 68 | { 69 | std::cout << "Create called\n"; 70 | 71 | CheckArgCount( L, 4 ); 72 | 73 | int x = luaL_checkinteger( L, 1 ); 74 | int y = luaL_checkinteger( L, 2 ); 75 | int w = luaL_checkinteger( L, 3 ); 76 | int h = luaL_checkinteger( L, 4 ); 77 | 78 | RectanglePtr sp = std::make_shared( x, y, w, h ); 79 | 80 | push( L, sp ); 81 | 82 | return 1; 83 | } 84 | 85 | // Method glue functions 86 | // 87 | 88 | static int isInside( lua_State *L ) 89 | { 90 | CheckArgCount( L, 3 ); 91 | 92 | RectanglePtr rect = fromStack( L, 1 ); 93 | int x = luaL_checkinteger( L, 2 ); 94 | int y = luaL_checkinteger( L, 3 ); 95 | 96 | lua_pushboolean( L, rect->isInside( x, y ) ); 97 | return 1; 98 | } 99 | 100 | // Property getters and setters 101 | 102 | // None. 103 | 104 | }; 105 | 106 | void test( lua_State* L ) 107 | { 108 | RectangleBinding::register_class( L ); 109 | 110 | if( luaL_dofile( L, "gui.lua" ) ) 111 | { 112 | cout << lua_tostring( L, -1 ) << endl; 113 | lua_pop( L, 1 ); 114 | return; 115 | } 116 | 117 | if( luaL_dostring( L, "test()" ) ) 118 | { 119 | cout << lua_tostring( L, -1 ) << endl; 120 | lua_pop( L, 1 ); 121 | return; 122 | } 123 | 124 | return; 125 | } 126 | 127 | int main() 128 | { 129 | lua_State* L = luaL_newstate(); 130 | 131 | luaL_openlibs( L ); 132 | 133 | test( L ); 134 | 135 | lua_close( L ); 136 | 137 | return EXIT_SUCCESS; 138 | } 139 | -------------------------------------------------------------------------------- /examples/demo/MyVector3dBinding.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYVECTOR3DBINDING_H 2 | #define __MYVECTOR3DBINDING_H 3 | #include "LuaBinding.h" 4 | #include "MyVector3d.h" 5 | 6 | using namespace ManualBind; 7 | 8 | struct MyVector3dBinding: public PODBinding { 9 | 10 | static constexpr const char* class_name = "MyVector3d"; 11 | 12 | static luaL_Reg* members() 13 | { 14 | static luaL_Reg members[] = { 15 | { "__gc", destroy }, // PODs usually don't have destructors, 16 | // but if they do then do this. 17 | // destroy is defined in PODBinding. 18 | { nullptr, nullptr } 19 | }; 20 | return members; 21 | } 22 | 23 | static bind_properties* properties() { 24 | static bind_properties properties[] = { 25 | { "x", get, set }, 26 | { "y", get, set }, 27 | { "z", get, set }, 28 | { nullptr, nullptr, nullptr } 29 | }; 30 | return properties; 31 | } 32 | 33 | static void setExtraMeta( lua_State* L ) 34 | { 35 | MyVector3d zero = MyVector3d( 0, 0, 0 ); 36 | 37 | push( L, zero ); 38 | lua_setfield( L, -2, "ZERO" ); // Class's meta table 39 | return; 40 | } 41 | 42 | // Lua constructor 43 | static int create( lua_State* L ) 44 | { 45 | std::cout << "Create called\n"; 46 | 47 | CheckArgCount( L, 3 ); 48 | 49 | float x = luaL_checknumber( L, 1 ); 50 | float y = luaL_checknumber( L, 2 ); 51 | float z = luaL_checknumber( L, 3 ); 52 | 53 | MyVector3d vec( x, y, z ); 54 | 55 | push( L, vec ); 56 | 57 | return 1; 58 | } 59 | 60 | // Method glue functions 61 | // 62 | 63 | // [none] 64 | 65 | // Property getters and setters 66 | static const char *prop_keys[]; 67 | 68 | // 1 - class metatable 69 | // 2 - key 70 | static int get( lua_State *L ) 71 | { 72 | CheckArgCount( L, 2 ); 73 | 74 | MyVector3d& v = fromStack( L, 1 ); 75 | 76 | int which = luaL_checkoption( L, 2, nullptr, MyVector3dBinding::prop_keys ); 77 | 78 | switch( which ) 79 | { 80 | case 0: 81 | lua_pushnumber( L, v.x ); 82 | break; 83 | case 1: 84 | lua_pushnumber( L, v.y ); 85 | break; 86 | case 2: 87 | lua_pushnumber( L, v.z ); 88 | break; 89 | default: 90 | luaL_argerror( L, 2, "What?" ); 91 | break; 92 | } 93 | 94 | return 1; 95 | } 96 | 97 | // 1 - class metatable 98 | // 2 - key 99 | // 3 - value 100 | static int set( lua_State *L ) 101 | { 102 | CheckArgCount( L, 3 ); 103 | 104 | MyVector3d& v = fromStack( L, 1 ); 105 | 106 | int which = luaL_checkoption( L, 2, nullptr, MyVector3dBinding::prop_keys ); 107 | 108 | float value = lua_tonumber( L, 3 ); 109 | 110 | switch( which ) 111 | { 112 | case 0: 113 | v.x = value; 114 | break; 115 | case 1: 116 | v.y = value; 117 | break; 118 | case 2: 119 | v.z = value; 120 | break; 121 | default: 122 | luaL_argerror( L, 2, "What?" ); 123 | break; 124 | } 125 | return 0; 126 | } 127 | 128 | }; 129 | 130 | const char *MyVector3dBinding::prop_keys[] = { "x", "y", "z", nullptr }; 131 | 132 | #endif // __MYVECTOR3DBINDING_H 133 | -------------------------------------------------------------------------------- /examples/luaref/luaref.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | #include "LuaRef.h" 5 | 6 | using namespace std; 7 | using namespace ManualBind; 8 | 9 | LuaRef getTesting( lua_State* L ) 10 | { 11 | lua_getglobal( L, "testing" ); 12 | return LuaRef::fromStack(L); 13 | } 14 | 15 | void printString( const std::string& str ) 16 | { 17 | cout << str << endl; 18 | } 19 | 20 | int main() 21 | { 22 | lua_State* L = luaL_newstate(); 23 | luaL_openlibs(L); 24 | 25 | run( L, "function testing( ... ) print( '> ', ... ) end" ); 26 | 27 | { 28 | LuaRef testing( L, "testing" ); 29 | LuaRef table = LuaRef::newTable( L ); 30 | table["testing"] = testing; 31 | 32 | table.push(); 33 | lua_setglobal( L, "a" ); 34 | 35 | run( L, "print( a.testing )" ); 36 | run( L, "a.b = {}" ); 37 | run( L, "a.b.c = {}" ); 38 | 39 | cout << "Is table a table? " 40 | << (table.isTable() ? "true" : "false" ) 41 | << endl; 42 | cout << "Is table[\"b\"] a table? " 43 | << (table["b"].isTable() ? "true" : "false" ) 44 | << endl; 45 | 46 | table["b"]["c"]["hello"] = "World!"; 47 | // A lot happens here behind the scenes. 48 | // The previous line is effectively the same as: 49 | // { 50 | // temp = table["b"]; 51 | // temp2 = temp["c"]; 52 | // temp3 = temp2["hello"]; 53 | // temp3 = "World!"; // Calls overloaded = operator. 54 | // // Here temps go out of scope and are destructed. 55 | // } 56 | // 57 | // Each temp is a pair of luaL_ref and luaL_unref's. 58 | // 59 | // If you are in a tight loop you will want to do this... 60 | // auto hello = table["b"]["c"]["hello"]; 61 | // for( ... ) { 62 | // hello = something( ..., ..., ... ); 63 | // whatever( hello ); 64 | // } 65 | 66 | run( L, "print( a.b.c.hello )" ); 67 | 68 | auto b = table["b"]; // returns a LuaTableElement 69 | b[3] = "Index 3"; 70 | 71 | LuaRef faster_b = b; // Convert LuaTableElement to LuaRef for faster pushing 72 | 73 | for( int i = 1; i < 5; i++ ) 74 | { 75 | faster_b.append( i ); 76 | } 77 | b[1] = LuaNil(); 78 | b.append( "Add more." ); 79 | 80 | run( L, "for k,v in pairs( a.b ) do print( k,v ) end" ); 81 | 82 | table["b"] = LuaNil(); 83 | 84 | run( L, "print( a.b )" ); 85 | 86 | testing(); 87 | testing( 1, 2, 3 ); 88 | testing( "Hello", "World" ); 89 | 90 | testing( "Hello", "World", 1, 2, 3, testing ); 91 | 92 | testing( "Nigel", "Alara", "Aldora", "Ayna", 93 | "Sarah", "Gavin", "Joe", "Linda", 94 | "Tom", "Sonja", "Greg", "Trish" 95 | ); 96 | 97 | // No return value 98 | testing.call( 0, "No return value." ); 99 | 100 | table["testing"](testing,3,2,1,"Calling array element"); 101 | table["testing"](); 102 | 103 | LuaRef newfuncref( L ); 104 | 105 | newfuncref = testing; 106 | 107 | newfuncref( "Did it copy correctly?" ); 108 | 109 | newfuncref( getTesting( L ) ); // Check move semantics 110 | 111 | newfuncref = getTesting( L ); // Check move semantics 112 | 113 | run( L, "text = 'This has been implicitly cast to std::string'" ); 114 | 115 | LuaRef luaStr1( L, "text" ); 116 | 117 | std::string str1 = luaStr1; 118 | 119 | printString( str1 ); 120 | 121 | run( L, "a.text = text" ); 122 | 123 | printString( table["text"] ); 124 | 125 | } 126 | 127 | lua_close(L); 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /src/LuaException.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | /* 3 | https://github.com/vinniefalco/LuaBridge 4 | 5 | Copyright 2012, Vinnie Falco 6 | Copyright 2008, Nigel Atkinson 7 | 8 | License: The MIT License (http://www.opensource.org/licenses/mit-license.php) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | //============================================================================== 29 | 30 | #ifndef __LUAEXCEPTION_H 31 | #define __LUAEXCEPTION_H 32 | 33 | #include 34 | 35 | namespace ManualBind { 36 | 37 | class LuaException : public std::exception 38 | { 39 | private: 40 | lua_State* m_L; 41 | std::string m_what; 42 | 43 | public: 44 | //---------------------------------------------------------------------------- 45 | LuaException (lua_State* L, int /*code*/) 46 | : m_L (L) 47 | { 48 | whatFromStack (); 49 | } 50 | 51 | LuaException( std::string what ) : m_L(nullptr) 52 | { 53 | m_what = what; 54 | } 55 | 56 | //---------------------------------------------------------------------------- 57 | 58 | LuaException (lua_State *L, 59 | char const*, 60 | char const*, 61 | long) 62 | : m_L (L) 63 | { 64 | whatFromStack (); 65 | } 66 | 67 | //---------------------------------------------------------------------------- 68 | 69 | ~LuaException() throw () 70 | { 71 | } 72 | 73 | //---------------------------------------------------------------------------- 74 | 75 | char const* what() const throw () 76 | { 77 | return m_what.c_str(); 78 | } 79 | 80 | //============================================================================ 81 | /** 82 | Throw an exception. 83 | 84 | This centralizes all the exceptions thrown, so that we can set 85 | breakpoints before the stack is unwound, or otherwise customize the 86 | behavior. 87 | */ 88 | template 89 | static void Throw (Exception e) 90 | { 91 | throw e; 92 | } 93 | 94 | //---------------------------------------------------------------------------- 95 | /** 96 | Wrapper for lua_pcall that throws. 97 | */ 98 | static void pcall (lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0) 99 | { 100 | int code = lua_pcall (L, nargs, nresults, msgh); 101 | 102 | if (code != LUA_OK) 103 | Throw (LuaException (L, code)); 104 | } 105 | 106 | //---------------------------------------------------------------------------- 107 | 108 | protected: 109 | void whatFromStack () 110 | { 111 | if (lua_gettop (m_L) > 0) 112 | { 113 | char const* s = lua_tostring (m_L, -1); 114 | m_what = s ? s : ""; 115 | } 116 | else 117 | { 118 | // stack is empty 119 | m_what = "missing error"; 120 | } 121 | } 122 | }; 123 | 124 | }; // namespace ManualBind 125 | 126 | #endif // __LUAEXCEPTION_H 127 | -------------------------------------------------------------------------------- /examples/downcast/downcast.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include "LuaBinding.h" 4 | 5 | // Down casting example. 6 | // Not often needed, but here is how to do it, while maintaining 7 | // correct refcounts in the shared pointers. 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | class Vehicle 13 | { 14 | public: 15 | virtual bool Start() { 16 | cout << "Engine running!" << endl; 17 | return true; 18 | } 19 | 20 | virtual ~Vehicle() { 21 | } 22 | }; 23 | 24 | class Car: public Vehicle 25 | { 26 | public: 27 | bool LoadGroceries() { 28 | cout << "Grocieries have been loaded." << endl; 29 | return true; 30 | } 31 | }; 32 | 33 | /****************************** Bindings ***************************/ 34 | 35 | using VehiclePtr = std::shared_ptr; 36 | using CarPtr = std::shared_ptr; 37 | 38 | using namespace ManualBind; 39 | 40 | struct VehicleBinding: public Binding { 41 | 42 | static constexpr const char* class_name = "Vehicle"; 43 | 44 | static luaL_Reg* members() 45 | { 46 | static luaL_Reg members[] = { 47 | { "Start", Start }, 48 | { nullptr, nullptr } 49 | }; 50 | return members; 51 | } 52 | 53 | static int create( lua_State *L ) 54 | { 55 | VehiclePtr vp = std::make_shared(); 56 | push( L, vp ); 57 | return 1; 58 | } 59 | 60 | static int Start( lua_State *L ) 61 | { 62 | VehiclePtr vp = fromStack( L, 1 ); 63 | lua_pushboolean( L, vp->Start() ); 64 | return 1; 65 | } 66 | }; 67 | 68 | struct CarBinding: public Binding { 69 | 70 | static constexpr const char* class_name = "Car"; 71 | 72 | static luaL_Reg* members() 73 | { 74 | static luaL_Reg members[] = { 75 | { "Start", Start }, 76 | { "LoadGroceries", LoadGroceries }, 77 | { nullptr, nullptr } 78 | }; 79 | return members; 80 | } 81 | 82 | static int create( lua_State *L ) 83 | { 84 | CarPtr cp = std::make_shared(); 85 | push( L, cp ); 86 | return 1; 87 | } 88 | 89 | static int Start( lua_State *L ) 90 | { 91 | CarPtr cp = fromStack( L, 1 ); 92 | lua_pushboolean( L, cp->Start() ); 93 | return 1; 94 | } 95 | 96 | static int LoadGroceries( lua_State *L ) 97 | { 98 | CarPtr cp = fromStack( L, 1 ); 99 | lua_pushboolean( L, cp->LoadGroceries() ); 100 | return 1; 101 | } 102 | 103 | }; 104 | 105 | int downcast( lua_State *L ) 106 | { 107 | VehiclePtr vp = VehicleBinding::fromStack( L, 1 ); 108 | 109 | // This may return nullptr, if the class pointed to by vp 110 | // is not a Car, and the refcount will not be updated. 111 | CarPtr cp = std::dynamic_pointer_cast( vp ); 112 | 113 | CarBinding::push( L, cp ); 114 | 115 | return 1; 116 | } 117 | 118 | int main() 119 | { 120 | lua_State* L = luaL_newstate(); 121 | luaL_openlibs( L ); 122 | 123 | VehicleBinding::register_class( L ); 124 | CarBinding::register_class( L ); 125 | 126 | lua_register( L, "downcast", downcast ); 127 | 128 | VehiclePtr newcar(new Car()); 129 | 130 | VehicleBinding::push( L, newcar ); 131 | lua_setglobal( L, "newcar" ); 132 | 133 | run( L, "newcar:Start()" ); 134 | cout << "Newcar use count: " << newcar.use_count() << endl; 135 | 136 | run( L, "newcar:LoadGroceries()" ); 137 | cout << "Newcar use count: " << newcar.use_count() << endl; 138 | 139 | run( L, "p = downcast( newcar )" ); 140 | // Use count should be higher. 141 | cout << "Newcar use count: " << newcar.use_count() << endl; 142 | 143 | run( L, "p:LoadGroceries()" ); 144 | cout << "Newcar use count: " << newcar.use_count() << endl; 145 | 146 | VehiclePtr motorbike(new Vehicle()); 147 | VehicleBinding::push( L, motorbike ); 148 | lua_setglobal( L, "motorbike" ); 149 | 150 | run( L, "motorbike:Start()" ); 151 | cout << "Motorbike use count: " << motorbike.use_count() << endl; 152 | 153 | run( L, "p = downcast( motorbike )" ); 154 | cout << "Motorbike use count: " << motorbike.use_count() << endl; 155 | 156 | run( L, "if p == nil then print( 'Motorbike is not a car. Can not load groceries. Go have fun instead.' ) end" ); 157 | run( L, "print(p)" ); 158 | 159 | lua_close(L); 160 | // Both of these should be back down to 1. 161 | cout << "Newcar use count: " << newcar.use_count() << endl; 162 | cout << "Motorbike use count: " << motorbike.use_count() << endl; 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /examples/upcast/upcast.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include 4 | #include 5 | #include "LuaBinding.h" 6 | 7 | // Up casting example. 8 | // Needed when you need to call methods or functions expecting 9 | // a base class when you have a derived class. You might do this when 10 | // you need to process different derived classes in the same way, 11 | // for example rendering different GUI elements in a list. 12 | // Maintains correct refcounts in the shared pointers. 13 | 14 | using namespace ManualBind; 15 | using std::cout; 16 | using std::endl; 17 | using std::hex; 18 | using std::dec; 19 | 20 | // base / interface (doesn't have to be abstract) 21 | class Renderable 22 | { 23 | public: 24 | virtual bool Render() = 0; 25 | }; 26 | 27 | class Square: public Renderable 28 | { 29 | public: 30 | bool Render() { 31 | cout << hex << this << dec << " - I'm a Square!" << endl; 32 | return true; 33 | } 34 | virtual ~Square() { 35 | } 36 | }; 37 | 38 | class Circle: public Renderable 39 | { 40 | public: 41 | bool Render() { 42 | cout << hex << this << dec << " - I'm a Circle!" << endl; 43 | return true; 44 | } 45 | virtual ~Circle() { 46 | } 47 | }; 48 | 49 | using RenderablePtr = std::shared_ptr; 50 | using SquarePtr = std::shared_ptr; 51 | using CirclePtr = std::shared_ptr; 52 | using RenderList = std::vector; 53 | 54 | void RenderAll( const RenderList& rl ) 55 | { 56 | cout << "Rendering list:" << endl; 57 | 58 | for( const auto& obj : rl ) 59 | { 60 | obj->Render(); 61 | } 62 | } 63 | 64 | void AddObject( RenderList& rl, RenderablePtr& rp ) 65 | { 66 | rl.push_back( rp ); 67 | } 68 | 69 | /****************************** Bindings ***************************/ 70 | 71 | 72 | struct RenderableBinding: public Binding { 73 | 74 | static constexpr const char* class_name = "Renderable"; 75 | 76 | }; 77 | 78 | struct SquareBinding: public Binding { 79 | 80 | static constexpr const char* class_name = "Square"; 81 | 82 | static luaL_Reg* members() 83 | { 84 | static luaL_Reg members[] = { 85 | { "__upcast", upcast }, 86 | { nullptr, nullptr } 87 | }; 88 | return members; 89 | } 90 | 91 | static int create( lua_State *L ) 92 | { 93 | SquarePtr p = std::make_shared(); 94 | push( L, p ); 95 | return 1; 96 | } 97 | 98 | static int upcast( lua_State *L ) 99 | { 100 | SquarePtr p = fromStack( L, 1 ); 101 | 102 | RenderablePtr rp = std::dynamic_pointer_cast( p ); 103 | 104 | RenderableBinding::push( L, rp ); 105 | 106 | return 1; 107 | } 108 | }; 109 | 110 | struct CircleBinding: public Binding { 111 | 112 | static constexpr const char* class_name = "Circle"; 113 | 114 | static luaL_Reg* members() 115 | { 116 | static luaL_Reg members[] = { 117 | { "__upcast", upcast }, 118 | { nullptr, nullptr } 119 | }; 120 | return members; 121 | } 122 | 123 | static int create( lua_State *L ) 124 | { 125 | CirclePtr p = std::make_shared(); 126 | push( L, p ); 127 | return 1; 128 | } 129 | 130 | static int upcast( lua_State *L ) 131 | { 132 | CirclePtr p = fromStack( L, 1 ); 133 | 134 | RenderablePtr rp = std::dynamic_pointer_cast( p ); 135 | 136 | RenderableBinding::push( L, rp ); 137 | 138 | return 1; 139 | } 140 | }; 141 | 142 | // Lua render list operations on a global list... 143 | // Normally the list would not be global but in the interest 144 | // of making this example smaller... 145 | 146 | RenderList gRenderList; 147 | 148 | int lua_render_all( lua_State* L ) 149 | { 150 | RenderAll( gRenderList ); 151 | return 0; 152 | } 153 | 154 | int lua_add_object( lua_State* L ) 155 | { 156 | // Before upcast'ing, you may want to check if the 157 | // class is the type you want already. 158 | // In this example the base is abstract so it will 159 | // not happen. 160 | 161 | // We are expecting an object with an __upcast function 162 | // returning a Renderable. 163 | LuaBindingUpCast( L, 1 ); 164 | 165 | // Here you could check for nil, meaning the upcast failed. 166 | 167 | RenderablePtr rp = RenderableBinding::fromStack( L, 1 ); 168 | 169 | AddObject( gRenderList, rp ); 170 | 171 | return 0; 172 | } 173 | 174 | int main() 175 | { 176 | lua_State* L = luaL_newstate(); 177 | luaL_openlibs( L ); 178 | 179 | RenderableBinding::register_class( L ); 180 | SquareBinding::register_class( L ); 181 | CircleBinding::register_class( L ); 182 | 183 | lua_register( L, "add", lua_add_object ); 184 | lua_register( L, "render", lua_render_all ); 185 | 186 | run( L, "print 'Upcast Example'" ); 187 | run( L, "add( Square() )" ); 188 | run( L, "add( Circle() )" ); 189 | run( L, "add( Circle() )" ); 190 | run( L, "add( Square() )" ); 191 | run( L, "render()" ); 192 | 193 | lua_close(L); 194 | 195 | return 0; 196 | } 197 | -------------------------------------------------------------------------------- /compile_commands.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "directory": "/home/nigel/prog/manualbind/build/examples", 4 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/exgui.dir/guiexample/guiexample.cpp.o -c /home/nigel/prog/manualbind/examples/guiexample/guiexample.cpp", 5 | "file": "/home/nigel/prog/manualbind/examples/guiexample/guiexample.cpp" 6 | }, 7 | { 8 | "directory": "/home/nigel/prog/manualbind/build/examples", 9 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/exupcast.dir/upcast/upcast.cpp.o -c /home/nigel/prog/manualbind/examples/upcast/upcast.cpp", 10 | "file": "/home/nigel/prog/manualbind/examples/upcast/upcast.cpp" 11 | }, 12 | { 13 | "directory": "/home/nigel/prog/manualbind/build/examples", 14 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/exdowncast.dir/downcast/downcast.cpp.o -c /home/nigel/prog/manualbind/examples/downcast/downcast.cpp", 15 | "file": "/home/nigel/prog/manualbind/examples/downcast/downcast.cpp" 16 | }, 17 | { 18 | "directory": "/home/nigel/prog/manualbind/build/examples", 19 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/extables.dir/tables/tables.cpp.o -c /home/nigel/prog/manualbind/examples/tables/tables.cpp", 20 | "file": "/home/nigel/prog/manualbind/examples/tables/tables.cpp" 21 | }, 22 | { 23 | "directory": "/home/nigel/prog/manualbind/build/examples", 24 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/exluaref.dir/luaref/luaref.cpp.o -c /home/nigel/prog/manualbind/examples/luaref/luaref.cpp", 25 | "file": "/home/nigel/prog/manualbind/examples/luaref/luaref.cpp" 26 | }, 27 | { 28 | "directory": "/home/nigel/prog/manualbind/build/examples", 29 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/exdemo.dir/demo/demo.cpp.o -c /home/nigel/prog/manualbind/examples/demo/demo.cpp", 30 | "file": "/home/nigel/prog/manualbind/examples/demo/demo.cpp" 31 | }, 32 | { 33 | "directory": "/home/nigel/prog/manualbind/build/examples", 34 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/common.dir/common/common.cpp.o -c /home/nigel/prog/manualbind/examples/common/common.cpp", 35 | "file": "/home/nigel/prog/manualbind/examples/common/common.cpp" 36 | }, 37 | { 38 | "directory": "/home/nigel/prog/manualbind/build/examples", 39 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/examples/../src -I/home/nigel/prog/manualbind/examples/common -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/common.dir/__/src/LuaBinding.cpp.o -c /home/nigel/prog/manualbind/src/LuaBinding.cpp", 40 | "file": "/home/nigel/prog/manualbind/src/LuaBinding.cpp" 41 | }, 42 | { 43 | "directory": "/home/nigel/prog/manualbind/build/tests", 44 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/main.cpp.o -c /home/nigel/prog/manualbind/tests/main.cpp", 45 | "file": "/home/nigel/prog/manualbind/tests/main.cpp" 46 | }, 47 | { 48 | "directory": "/home/nigel/prog/manualbind/build/tests", 49 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test1.cpp.o -c /home/nigel/prog/manualbind/tests/test1.cpp", 50 | "file": "/home/nigel/prog/manualbind/tests/test1.cpp" 51 | }, 52 | { 53 | "directory": "/home/nigel/prog/manualbind/build/tests", 54 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test2.cpp.o -c /home/nigel/prog/manualbind/tests/test2.cpp", 55 | "file": "/home/nigel/prog/manualbind/tests/test2.cpp" 56 | }, 57 | { 58 | "directory": "/home/nigel/prog/manualbind/build/tests", 59 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test_calling.cpp.o -c /home/nigel/prog/manualbind/tests/test_calling.cpp", 60 | "file": "/home/nigel/prog/manualbind/tests/test_calling.cpp" 61 | }, 62 | { 63 | "directory": "/home/nigel/prog/manualbind/build/tests", 64 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test_extras.cpp.o -c /home/nigel/prog/manualbind/tests/test_extras.cpp", 65 | "file": "/home/nigel/prog/manualbind/tests/test_extras.cpp" 66 | }, 67 | { 68 | "directory": "/home/nigel/prog/manualbind/build/tests", 69 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test_luaref.cpp.o -c /home/nigel/prog/manualbind/tests/test_luaref.cpp", 70 | "file": "/home/nigel/prog/manualbind/tests/test_luaref.cpp" 71 | }, 72 | { 73 | "directory": "/home/nigel/prog/manualbind/build/tests", 74 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/test_properties.cpp.o -c /home/nigel/prog/manualbind/tests/test_properties.cpp", 75 | "file": "/home/nigel/prog/manualbind/tests/test_properties.cpp" 76 | }, 77 | { 78 | "directory": "/home/nigel/prog/manualbind/build/tests", 79 | "command": "/usr/bin/c++ -I/home/nigel/prog/manualbind/tests/../src -I/home/nigel/prog/lua-5.4.0/src -g -std=gnu++11 -o CMakeFiles/runtests.dir/__/src/LuaBinding.cpp.o -c /home/nigel/prog/manualbind/src/LuaBinding.cpp", 80 | "file": "/home/nigel/prog/manualbind/src/LuaBinding.cpp" 81 | } 82 | ] -------------------------------------------------------------------------------- /examples/guiexample/gui.lua: -------------------------------------------------------------------------------- 1 | print( 'Using ' .. _VERSION ) 2 | -- Mouse enumeration 3 | Mouse = {} 4 | Mouse.Button = {} 5 | Mouse.Button.Left = 1 6 | Mouse.Button.Right = 2 7 | 8 | -- Widget Base Class 9 | -- This provides members and methods for all GUI objects that will be feed user input. 10 | -- These objects usally represent an interface item, but in some rare cases may not have 11 | -- any visual element at all. 12 | -- More specialised classes will be derived from this one. 13 | -- 14 | Widget = {} 15 | Widget.__index = Widget 16 | 17 | -- Some Syntactic Sugar, calling a Class table is like calling it's create method. 18 | function Widget.__call( class, ...) 19 | print( ... ) 20 | return class.Create(...) 21 | end 22 | 23 | -- Initialisation for all widgets 24 | function Widget:init( x, y, w, h, name ) 25 | print( 'Initialising ' .. name ) 26 | self.name = name 27 | self.rect = GUIRectangle( x, y, w, h ) 28 | self.key = '' 29 | self.hover = false 30 | self.children = {} 31 | end 32 | 33 | -- Normally these should do nothing unless overridden, however we're doing a demo here! 34 | function Widget:OnAction() print( self.name .. ' OnAction' ) end 35 | function Widget:OnKeyPress() print( self.name .. ' OnKeyPress' ) return self:OnAction() end 36 | function Widget:OnClick() print( self.name .. ' OnClick' ) return self:OnAction() end 37 | function Widget:OnAlternateClick() print( self.name .. ' OnAlternateClick' ) end 38 | function Widget:OnHover() print( self.name .. ' OnHover' ) end 39 | function Widget:OnHoverExit() print( self.name .. ' OnHoverExit' ) end 40 | 41 | -- Does this widget respond to this key? No? What about it's children? 42 | function Widget:KeyPress( key ) 43 | if key == self.key then 44 | return self:OnKeyPress() 45 | else 46 | for k, v in pairs( self.children ) do 47 | if v:KeyPress( key ) then 48 | return true 49 | end 50 | end 51 | end 52 | end 53 | 54 | -- Check if a mouse click is inside the rectangle that is our widget. 55 | -- If it is, see if there is a child ( which should fit inside ) that 56 | -- is a more specific match for the click position. 57 | -- Otherwise respond ourselves. 58 | -- 59 | -- If the click is outside, ignore it. 60 | -- 61 | function Widget:MouseClick( button, x, y ) 62 | print( 'Mouse clicked', self.name, button, x, y ) 63 | if self.rect:isInside( x, y ) then 64 | for k, v in pairs( self.children ) do 65 | if v:MouseClick( button, x, y ) then 66 | return true 67 | end 68 | end 69 | 70 | print( self.name .. ' No child handled click' ) 71 | 72 | if button == Mouse.Button.Left then 73 | return self:OnClick() 74 | elseif button == Mouse.Button.Right then 75 | return self:OnAlternateClick() 76 | end 77 | end 78 | end 79 | 80 | function Widget:MouseMove( x, y ) 81 | print( self.name .. ' Mouse moved', x, y ) 82 | if self.rect:isInside( x, y ) then 83 | print( ' Inside ' .. self.name ) 84 | if self.hover == false then 85 | self.hover = true 86 | self:OnHover() 87 | end 88 | for k, v in pairs( self.children ) do 89 | v:MouseMove( x, y ) 90 | end 91 | else 92 | print( ' Outside ' .. self.name ) 93 | if self.hover == true then 94 | self.hover = false 95 | self:OnHoverExit() 96 | for k, v in pairs( self.children ) do 97 | v:MouseLost() 98 | end 99 | end 100 | end 101 | end 102 | 103 | -- Simpler, faster, version of MouseMove that assumes 104 | -- that as the mouse is outside the parent widget, it is 105 | -- outside this widget, and therefor the mouse is lost, 106 | -- and informs it's children. 107 | -- 108 | function Widget:MouseLost() 109 | print( self.name .. ' has lost the mouse.' ) 110 | if self.hover then 111 | self.hover = false 112 | self:OnHoverExit() 113 | for k, v in pairs( self.children ) do 114 | v:MouseLost() 115 | end 116 | end 117 | end 118 | 119 | -- Add a child widget. Child widgets should fit inside their parents 120 | -- for the mouse targetting to work correctly. 121 | -- 122 | function Widget:AddChild( child ) 123 | if type( child.MouseMove ) == 'function' then -- Very basic check 124 | table.insert( self.children, child ) 125 | child.parent = self 126 | else 127 | error( 'Not a valid type of Widget!' ) 128 | end 129 | end 130 | 131 | -- A Push Button Widget. 132 | Button = {} 133 | Button.__index = Button 134 | Button.__base = Widget 135 | setmetatable( Button, Widget ) 136 | 137 | function Button:init( x, y, text ) 138 | Button.__base.init( self, x, y, 100, 50, 'Button ' .. text ) 139 | self.text = text 140 | end 141 | 142 | -- Override default Widget methods to give default Button actions! 143 | function Button:OnAction() 144 | print( self.name .. " I've been activated!" ) 145 | return true -- Event handled 146 | end 147 | 148 | function Button:OnHover() 149 | print( self.name .. ' Light up' ) 150 | end 151 | 152 | function Button:OnHoverExit() 153 | print( self.name .. ' Extinguished!' ) 154 | end 155 | 156 | function Button.Create( x, y, text ) 157 | local btn = {} 158 | setmetatable( btn, Button ) 159 | btn:init( x, y, text ) 160 | return btn 161 | end 162 | 163 | -- A dialog box widget to hold buttons. 164 | -- 165 | DialogBox = {} 166 | DialogBox.__index = DialogBox 167 | DialogBox.__base = Widget 168 | setmetatable( DialogBox, Widget ) 169 | 170 | function DialogBox.Create( x, y, w, h, title ) 171 | local dlg = {} 172 | setmetatable( dlg, DialogBox ) 173 | dlg:init( x, y, w, h, 'DialogBox ' .. title ) 174 | return dlg 175 | end 176 | 177 | 178 | ------------------------------------------------------------------------------- 179 | 180 | function test() 181 | -- A dialog box with two buttons. 182 | 183 | mybox = DialogBox( 10, 10, 500, 300, '"Save Game?"' ) 184 | ok = Button( 20, 150, 'Ok' ) 185 | cancel = Button( 150, 150, 'Cancel' ) 186 | mybox:AddChild( ok ) 187 | mybox:AddChild( cancel ) 188 | 189 | print ( 'User moves to outside dialog box' ) 190 | mybox:MouseMove( 5, 5 ) 191 | print( '-------------------------------------' ) 192 | 193 | print ( 'User moves mouse over "ok"' ) 194 | mybox:MouseMove( 22, 153 ) 195 | print( '-------------------------------------' ) 196 | 197 | print ( 'User moves to "cancel"' ) 198 | mybox:MouseMove( 155, 156 ) 199 | print( '-------------------------------------' ) 200 | 201 | print ( 'User clicks left button' ) 202 | mybox:MouseClick( Mouse.Button.Left, 156, 154 ) 203 | print( '-------------------------------------' ) 204 | 205 | end 206 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ManualBind 2 | ========== 3 | 4 | [Licence](../LICENSE) | [Tutorial](quick%20tutorial) | [Todo List](TODO) 5 | 6 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 7 | 8 | Semi-manual binding between C++ and Lua. 9 | See header files and LICENSE for author and license info. 10 | 11 | *Update December 2025.* 12 | I've checked this still works with Lua 5.5 rc4. All tests pass, and the examples are working. 13 | I'm still using this library a lot, mainly with my 'disorganiser' project. 14 | 15 | *Update December 2020.* 16 | I've checked this still works with Lua 5.4. All tests pass, and the examples are working. 17 | 18 | Examples 19 | -------- 20 | 21 | There are several examples, which show various parts or techniques. 22 | 23 | - Demo. Runs LuaBinding.h through it's paces. 24 | - Downcast. How to cast a Lua held class instance to another class that is lower in the hierarchy. 25 | - Upcast. Opposite of downcast. Useful if you are storing instances in a C++ container. 26 | - GUI Example. How a simple GUI might be implemented in Lua, given a simple rectangle class binding. 27 | - LuaRef. Exercises the new LuaRef code. 28 | - Tables. How to push / pull a C++ container to a Lua table. 29 | 30 | LuaBinding.h 31 | ------------ 32 | 33 | Basically this makes the boring and difficult stuff easier, but leaving the more fun 34 | not so hard stuff to do. :-) 35 | 36 | The memory management, life-cycle, and method despatch is the same for each 37 | bound class, only the glue functions that marshal the parameters are 38 | different. 39 | 40 | Hand writing the glue functions also allows them to be as complicated or as 41 | simple as required. For example you could support optional arguments, or even 42 | overloading. 43 | 44 | Shared pointers are used to simplify the question of who owns what, and who 45 | should delete it. Class instances can be created in Lua or C++ and passed to 46 | the other freely, as the last one holding a reference will call the 47 | destructor when required. 48 | 49 | There is a POD (plain old data) version of the binding for things like pointers or 50 | tiny classes and structs. These get copied around unlike the shared pointer version which 51 | keeps one and only one copy. 52 | 53 | Extra elements can be assigned to a class instance as long as they do not conflict with an existing 54 | function or property. A table will be created for each class instance on the first value assigned, so 55 | if this feature is not used - no extra memory is required for a class instance. 56 | 57 | Methods can be overridden on a class in Lua code, however unlike extra elements, this will affect 58 | **all** instances, both existing and new. 59 | 60 | **Speed** 61 | Method lookup is the fastest, followed by properties, extra elements and finally the case of not found. 62 | Each case requires progressively more table lookups in addition to the order they are done. 63 | 64 | Example binding 65 | --------------- 66 | 67 | This is a binding from my 'disorganiser' project. It binds a class representing a rounded rectangle, 68 | to be drawn by SDL. This can be 'upcast' to it's base class 'Renderable' so that it can be added to 69 | a render list which may contain other graphic elements which all share Renderable as a base. 70 | 71 | No properties used on this one as I had no need to retreive values, however note that the last 72 | parameter (fillColor) of the constructor is optional. 73 | 74 | The tostring member function is used for debugging. 75 | 76 | ```c++ 77 | #ifndef __LB_ROUNDED_RECTANGLE_H 78 | #define __LB_ROUNDED_RECTANGLE_H 79 | 80 | #include "LuaBinding.h" 81 | #include "LuaRef.h" 82 | #include "bindings.h" 83 | #include "rounded_rectangle.h" 84 | #include "lb_renderlist.h" 85 | #include "lb_color.h" 86 | #include 87 | 88 | struct RoundedRectangleBinding : public ManualBind::Binding 89 | { 90 | static constexpr const char* class_name = "RoundedRectangle"; 91 | 92 | static luaL_Reg* members() 93 | { 94 | static luaL_Reg members[] = { 95 | { "__upcast", upcast }, 96 | { "__tostring", tostring }, 97 | { "setDest", setDest }, 98 | { "setColor", setColor }, 99 | { "setFill", setFill }, 100 | { "setRadius", setRadius }, 101 | { nullptr, nullptr } 102 | }; 103 | return members; 104 | } 105 | 106 | static int create(lua_State *L) 107 | { 108 | using ManualBind::LuaRef; 109 | 110 | SDL_Color &color = ColorBinding::fromStack(L, 1); 111 | SDL_Rect dest = getRectFromTable(L, 2); 112 | int radius = lua_tointeger(L, 3); 113 | 114 | if (lua_isnoneornil( L, 4)) 115 | { 116 | RoundedRectanglePtr rectangle = std::make_shared(color, dest, radius); 117 | 118 | push(L, rectangle); 119 | return 1; 120 | } 121 | 122 | SDL_Color &fillColor = ColorBinding::fromStack(L, 4); 123 | 124 | RoundedRectanglePtr rectangle = std::make_shared(color, dest, radius, fillColor); 125 | 126 | push(L, rectangle); 127 | 128 | return 1; 129 | } 130 | 131 | static int upcast( lua_State *L ) 132 | { 133 | RoundedRectanglePtr p = fromStack( L, 1 ); 134 | 135 | RenderablePtr rp = std::dynamic_pointer_cast( p ); 136 | 137 | RenderableBinding::push( L, rp ); 138 | 139 | return 1; 140 | } 141 | 142 | static int tostring( lua_State* L ) 143 | { 144 | RoundedRectanglePtr r = fromStack( L, 1 ); 145 | std::stringstream ss; 146 | 147 | ss << "Rounded Rectangle: " << std::hex << r.get() << std::dec; 148 | ss << " dest: { "; 149 | SDL_Rect rect = r->getDest(); 150 | ss << rect.x << ", " << rect.y << ", " << rect.w << ", " << rect.h; 151 | ss << "}"; 152 | 153 | lua_pushstring( L, ss.str().c_str() ); 154 | 155 | return 1; 156 | } 157 | 158 | static int setDest( lua_State* L ) 159 | { 160 | RoundedRectanglePtr p = fromStack( L, 1 ); 161 | SDL_Rect r = getRectFromTable( L, 2 ); 162 | 163 | p->setDest(r); 164 | return 0; 165 | } 166 | 167 | static int setColor( lua_State* L ) 168 | { 169 | RoundedRectanglePtr p = fromStack( L, 1 ); 170 | SDL_Color& c = ColorBinding::fromStack( L, 2 ); 171 | 172 | p->setColor(c); 173 | return 0; 174 | } 175 | 176 | static int setFill( lua_State* L ) 177 | { 178 | RoundedRectanglePtr p = fromStack( L, 1 ); 179 | bool fill = lua_toboolean( L, 2 ); 180 | 181 | p->setFill(fill); 182 | return 0; 183 | } 184 | 185 | static int setRadius( lua_State* L ) 186 | { 187 | RoundedRectanglePtr p = fromStack( L, 1 ); 188 | int radius = lua_tointeger( L, 2 ); 189 | 190 | p->setRadius(radius); 191 | return 0; 192 | } 193 | }; 194 | 195 | #endif //__LB_ROUNDED_RECTANGLE_H 196 | ``` 197 | 198 | LuaRef.h 199 | -------- 200 | 201 | C++ management of Lua data. A re-write of code originally written by me and then 202 | donated to LuaBridge. 203 | This version I have changed to use c++11 variable template arguments for 204 | calling Lua, The table element proxy is now public, and the code structure is 205 | re-arranged to put shared functionality between LuaRef, and TableElement in a 206 | base class. 207 | 208 | Allows you to write things like this in C++ 209 | 210 | ```c++ 211 | // Grab a ref to settings table. 212 | LuaRef settings = LuaRef( L, "settings" ); 213 | // Get the value of 'settings.display.width' 214 | int width = settings["display"]["width"]; 215 | // Set a setting 216 | settings["flux"]["capacitor"] = true; 217 | 218 | // Grab a ref to the print function 219 | LuaRef print = LuaRef( L, "print" ); 220 | // Use it like a C/C++ varadic function. 221 | print( 1, 2, "Hello", width, tbl ); 222 | ``` 223 | 224 | LuaException.h 225 | -------------- 226 | 227 | Support for throwing on Lua errors. Used by LuaRef. 228 | 229 | LuaStack.h 230 | ---------- 231 | 232 | From LuaBridge. The original LuaRef.h was changed to use this. I didn't want to reverse 233 | that. Originally called Stack.h. If I wrote this myself, it would look almost the same 234 | anyhow. :-) 235 | -------------------------------------------------------------------------------- /src/LuaBinding.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | /* 3 | 4 | Copyright 2017, Nigel Atkinson 5 | 6 | License: The MIT License (http://www.opensource.org/licenses/mit-license.php) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | //============================================================================== 27 | 28 | #include "LuaBinding.h" 29 | 30 | namespace ManualBind { 31 | 32 | // Creates (on demand) the table for storing 'extra' values per class. 33 | // Table has weak keys as to not prevent garbage collection of the instances. 34 | // Leaves the new table at the top of the stack. 35 | int createExtraValueStore( lua_State* L ) 36 | { 37 | lua_newtable( L ); 38 | lua_newtable( L ); 39 | lua_pushliteral( L, "__mode" ); 40 | lua_pushliteral( L, "k" ); 41 | lua_settable( L, -3 ); 42 | lua_setmetatable( L, -2 ); 43 | lua_pushcfunction( L, createExtraValueStore ); // Use function as GUID 44 | lua_pushvalue( L, -2 ); 45 | lua_settable( L, LUA_REGISTRYINDEX ); 46 | return 1; 47 | } 48 | 49 | // Called when Lua object is indexed: obj[ndx] 50 | int LuaBindingIndex( lua_State *L ) 51 | { 52 | // 1 - class user data 53 | // 2 - key 54 | lua_getmetatable( L, 1 ); 55 | // 3 - class metatable 56 | if( lua_isnumber( L, 2 ) ) { // Array access 57 | lua_getfield( L, 3, "__arrayindex" ); 58 | if( lua_type( L, 4 ) == LUA_TFUNCTION ) { 59 | lua_pushvalue( L, 1 ); 60 | lua_pushvalue( L, 2 ); 61 | lua_call( L, 2, 1 ); 62 | } else { 63 | luaL_error( L, "Attempt to index without __arrayindex" ); 64 | } 65 | return 1; 66 | } 67 | lua_pushvalue( L, 2 ); // Key 68 | lua_gettable( L, 3 ); 69 | if( lua_type( L, 4 ) != LUA_TNIL ) { // Found in metatable. 70 | return 1; 71 | } 72 | lua_pop( L, 1 ); 73 | lua_getfield( L, 3, "__properties" ); 74 | // 4 - class properties table 75 | lua_pushvalue( L, 2 ); 76 | lua_gettable( L, 4 ); 77 | // 5 - property table for key ( or nil ) 78 | if( lua_type( L, 5 ) == LUA_TTABLE ) { // Found in properties. 79 | lua_getfield( L, 5, "get" ); 80 | // Call get function. 81 | if( lua_type( L, 6 ) == LUA_TFUNCTION ) { 82 | lua_pushvalue( L, 1 ); 83 | lua_pushvalue( L, 2 ); 84 | lua_call( L, 2, 1 ); 85 | } 86 | return 1; 87 | } 88 | lua_pop( L, 2 ); // __properties 89 | lua_pushcfunction( L, createExtraValueStore ); 90 | lua_gettable( L, LUA_REGISTRYINDEX ); 91 | if( lua_type( L, 4 ) == LUA_TTABLE ) { 92 | lua_pushvalue( L, 1 ); 93 | lua_gettable( L, 4 ); 94 | if( lua_type( L, 5 ) == LUA_TTABLE ) { // Has added instance vars 95 | lua_pushvalue( L, 2 ); 96 | lua_gettable( L, 5 ); 97 | return 1; // Return whatever was found, possibly nil. 98 | } 99 | } 100 | 101 | lua_pushnil( L ); 102 | return 1; // Not found, return nil. 103 | } 104 | 105 | // Get the table holding any extra values for the class metatable 106 | // at index. Returns nil if there has not been any assigned, and 107 | // no table yet exists. 108 | int LuaBindingGetExtraValuesTable( lua_State* L, int index ) 109 | { 110 | index = lua_absindex( L, index ); 111 | lua_pushcfunction( L, createExtraValueStore ); 112 | lua_gettable( L, LUA_REGISTRYINDEX ); 113 | if( lua_type( L, -1 ) == LUA_TTABLE ) { 114 | lua_pushvalue( L, index ); 115 | lua_gettable( L, -2 ); 116 | lua_remove( L, -2 ); 117 | } 118 | return 1; 119 | } 120 | 121 | // Called whe Lua object index is assigned: obj[ndx] = blah 122 | int LuaBindingNewIndex( lua_State *L ) 123 | { 124 | // 1 - class user data 125 | // 2 - key 126 | // 3 - value 127 | lua_getmetatable( L, 1 ); 128 | if( lua_isnumber( L, 2 ) ) { // Array access 129 | lua_getfield( L, 4, "__arraynewindex" ); 130 | if( lua_type( L, 5 ) == LUA_TFUNCTION ) { 131 | lua_pushvalue( L, 1 ); 132 | lua_pushvalue( L, 2 ); 133 | lua_pushvalue( L, 3 ); 134 | lua_call( L, 3, 0 ); 135 | } else { 136 | luaL_error( L, "Attempt to assign to index without __arraynewindex" ); 137 | } 138 | return 0; 139 | } 140 | // 4 - class metatable 141 | // If it's in the metatable, then update it. 142 | lua_pushvalue( L, 2 ); 143 | lua_gettable( L, 4 ); 144 | if( lua_type( L, 5 ) != LUA_TNIL ) { // Found in metatable. 145 | lua_pushvalue( L, 2 ); 146 | lua_pushvalue( L, 3 ); 147 | lua_settable( L, 4 ); 148 | return 0; 149 | } 150 | lua_pop( L, 1 ); 151 | 152 | lua_getfield( L, 4, "__properties" ); 153 | // 5 - class properties table 154 | lua_pushvalue( L, 2 ); 155 | lua_gettable( L, 5 ); 156 | // 6 - property table for key ( or nil ) 157 | if( lua_type( L, 6 ) == LUA_TTABLE ) { // Found in properties. 158 | lua_getfield( L, 6, "set" ); 159 | if( lua_type( L, 7 ) == LUA_TFUNCTION ) { 160 | // Call set function. 161 | lua_pushvalue( L, 1 ); 162 | lua_pushvalue( L, 2 ); 163 | lua_pushvalue( L, 3 ); 164 | lua_call( L, 3, 0 ); 165 | } 166 | return 0; 167 | } 168 | lua_settop( L, 3 ); 169 | 170 | // set in per instance table 171 | lua_pushcfunction( L, createExtraValueStore ); 172 | lua_gettable( L, LUA_REGISTRYINDEX ); 173 | if( lua_type( L, 4 ) != LUA_TTABLE ) { 174 | lua_pop( L, 1 ); 175 | createExtraValueStore( L ); 176 | } 177 | lua_pushvalue( L, 1 ); 178 | lua_gettable( L, 4 ); 179 | if( lua_type( L, 5 ) != LUA_TTABLE ) { // No added instance table yet 180 | lua_pop( L, 1 ); 181 | lua_newtable( L ); 182 | 183 | lua_pushvalue( L, 1 ); 184 | lua_pushvalue( L, 5 ); 185 | lua_settable( L, 4 ); 186 | } 187 | 188 | // Set the value in the instance table. 189 | lua_pushvalue( L, 2 ); 190 | lua_pushvalue( L, 3 ); 191 | lua_settable( L, 5 ); 192 | return 0; 193 | } 194 | 195 | void LuaBindingSetProperties( lua_State *L, bind_properties* properties ) 196 | { 197 | // Assumes table at top of the stack for the properties. 198 | while( properties->name != nullptr ) { 199 | 200 | lua_newtable( L ); 201 | 202 | lua_pushcfunction( L, properties->getter ); 203 | lua_setfield( L, -2, "get" ); 204 | if( properties->setter ) { 205 | lua_pushcfunction( L, properties->setter ); 206 | lua_setfield( L, -2, "set" ); 207 | } 208 | lua_setfield( L, -2, properties->name ); 209 | 210 | properties++; 211 | } 212 | } 213 | 214 | // If the object at 'index' is a userdata with a metatable containing a __upcast 215 | // function, then replaces the userdata at 'index' in the stack with the result 216 | // of calling __upcast. 217 | // Otherwise the object at index is replaced with nil. 218 | int LuaBindingUpCast( lua_State* L, int index ) 219 | { 220 | void *p = lua_touserdata(L, index ); 221 | if( p != nullptr ) 222 | { 223 | if( lua_getmetatable( L, index ) ) { 224 | lua_getfield( L, -1, "__upcast" ); 225 | if( lua_type( L, -1 ) == LUA_TFUNCTION ) { 226 | // Call upcast 227 | lua_pushvalue( L, -3 ); 228 | lua_call( L, 1, 1 ); 229 | lua_replace( L, index ); 230 | lua_pop( L, 1 ); // Remove metatable. 231 | return 1; 232 | } 233 | } 234 | } 235 | lua_pushnil( L ); // Cannot be converted. 236 | lua_replace( L, index ); 237 | return 1; 238 | } 239 | 240 | 241 | // Check the number of arguments are as expected. 242 | // Throw an error if not. 243 | void CheckArgCount( lua_State *L, int expected ) 244 | { 245 | int n = lua_gettop(L); 246 | if( n != expected ) { 247 | luaL_error( L, "Got %d arguments, expected %d", n, expected ); 248 | return; 249 | } 250 | return; 251 | } 252 | 253 | }; // namespace ManualBind 254 | -------------------------------------------------------------------------------- /docs/quick tutorial.md: -------------------------------------------------------------------------------- 1 | Quick Tutorial 2 | ============== 3 | 4 | For this tutorial we will bind a simple class for Lua to use, progressively adding more features. 5 | 6 | Here's the class we will work with. 7 | 8 | ```c++ 9 | 10 | class Widget 11 | { 12 | private: 13 | std::string caption; 14 | int x; 15 | int y; 16 | 17 | public: 18 | Widget() 19 | {} 20 | Widget( std::string newCaption, int nx, int ny ) : caption(newCaption), x(nx), y(ny) 21 | {} 22 | void setCaption( std::string& newCaption ) 23 | { 24 | caption = newCaption; 25 | } 26 | std::string getCaption() 27 | { 28 | return caption; 29 | } 30 | void move( int nx, int ny ) 31 | { 32 | x = nx; 33 | y = ny; 34 | } 35 | int getX() { return x; } 36 | int getY() { return y; } 37 | void Show( bool upsideDown = false ); // Defined elsewhere. 38 | } 39 | 40 | ``` 41 | This class is not the best for binding, however I am going to pretend that for whatever reason, we are stuck with it. This is a common senario. 42 | 43 | First off, lets do a really simple binding, that allows us to hand a pointer to Lua, and Lua give it back to C++, with type checking. We will use the full shared pointer style binding, as we'll add more later. 44 | 45 | ```c++ 46 | 47 | struct WidgetBinding : public ManualBind::Binding 48 | { 49 | static constexpr const char* class_name = "Widget"; 50 | }; 51 | 52 | ``` 53 | This is all we need for now. It is a struct just to make everything public by default. ManualBind::Binding will supply defaults for the list of member functions, list of properties, and the constructor. 54 | The default constructor raises an error, saying you can not construct this object from Lua. 55 | 56 | To use this we need to register the binding to Lua. 57 | 58 | ```c++ 59 | 60 | lua_State* L = luaL_newstate(); 61 | ... 62 | WidgetBinding::register_class(L); 63 | ... 64 | ``` 65 | 66 | Now we can push and pull an instance of Widget to Lua as follows. We can't really do anything with it in Lua yet, except pass it back to C++. 67 | 68 | ```c++ 69 | using WidgetPtr = std::shared_ptr; 70 | 71 | WidgetPtr w = std::make_shared(); 72 | 73 | // Push and assign to global. 74 | WidgetBinding::push( L, w ); 75 | lua_setglobal( L, "widget1" ); 76 | 77 | lua_getglobal( L, "widget1" ); 78 | // Retrieve from the stack. 79 | WidgetPtr w = WidgetBinding::fromStackThrow( L, -1 ); 80 | ``` 81 | 82 | There are two versions of 'fromStack'. 'fromStackThrow' is used in C++ code not called by Lua, and will throw an exception if the item on the stack is not the class you are trying to get. 'fromStack' is for code called by Lua and will call luaL_error instead. 83 | 84 | So now we can pass an instance of a Widget to Lua, and retrieve it or Lua could call a C/C++ function with the instance. In rare cases this is all you will need. 85 | 86 | Let's give Lua the ability to make Widgets: 87 | ```c++ 88 | 89 | struct WidgetBinding : public ManualBind::Binding 90 | { 91 | static constexpr const char* class_name = "Widget"; 92 | 93 | static void create( lua_State* L ) 94 | { 95 | std::string newCaption( luaL_checkstring( L, 1 ) ); 96 | int nx = luaL_checkinteger( L, 2 ); 97 | int ny = luaL_checkinteger( L, 3 ); 98 | 99 | WidgetPtr w = std::make_shared( newCaption, nx, ny ); 100 | 101 | push( L, w ); 102 | 103 | return 1; 104 | } 105 | }; 106 | 107 | ``` 108 | 109 | This construction function will be automatically found by template magic and added to the binding. For this reason it must be called 'create'. Unlike most Lua Binding libraries, all 'glue' functions, i.e. this create and other functions we will write soon, need some familiarity with working with the Lua stack and Lua's C API. I've done this intentionally. This allows to allows you all the power of the Lua C API. 110 | 111 | Here we are simply grabbing each parameter from the Lua stack and using them to create a shared pointer to a brand new Widget. Then we push that pointer onto the Lua stack and return '1' as in the count of return values this function returns to Lua. The push function is the same one we saw earlier, albeit without the 'WidgetBinding::' qualifier. 112 | 113 | Now in Lua code we can do this: 114 | 115 | ```Lua 116 | w = Widget( 'Hello World', 5, 15 ) 117 | ``` 118 | 119 | Now that we can make Widgets from Lua, it would be nice to be able to call the 'show' method. 120 | 121 | We do this by adding another static function to the binding to override the default one which has no bound functions. We also need a small 'glue' function to marshal any parameters. 122 | ```c++ 123 | 124 | struct WidgetBinding : public ManualBind::Binding 125 | { 126 | static constexpr const char* class_name = "Widget"; 127 | 128 | static void create( lua_State* L ) 129 | { 130 | std::string newCaption( luaL_checkstring( L, 1 ) ); 131 | int nx = luaL_checkinteger( L, 2 ); 132 | int ny = luaL_checkinteger( L, 3 ); 133 | 134 | WidgetPtr w = std::make_shared( newCaption, nx, ny ); 135 | 136 | push( L, w ); 137 | 138 | return 1; 139 | } 140 | 141 | static luaL_Reg* members() 142 | { 143 | static luaL_Reg members[] = 144 | { 145 | { "show", show }, 146 | { nullptr, nullptr } 147 | }; 148 | return members; 149 | } 150 | 151 | static int show( lua_State* L ) 152 | { 153 | WidgetPtr w = fromStack( L, 1 ); 154 | // nil or not present will result in 'false'. Otherwise whatever lua_toboolean returns. 155 | bool upsideDown = luaL_opt( L, lua_toboolean, 2, false ); 156 | 157 | w->show( upsideDown ); 158 | 159 | return 0; 160 | } 161 | 162 | }; 163 | 164 | ``` 165 | The list in the members function is simply passed to the luaL_setfuncs. It follows the same rules, as does the function prototype for the 'show' glue function. This function grabs the 'self' or 'this' pointer from the stack and the optional boolean parameter and then calls the real member function. Note that unless you make the binding struct a friend of the class you are binding, you can only use it's public interface. 166 | 167 | In Lua we can now call the member function like this: 168 | ```Lua 169 | widget1:show() 170 | -- or -- 171 | widget1:show(true) 172 | ``` 173 | Note the colon, rather than dot as per usual Lua convention. This is functionally equivalent to 'widget1.show(widget1)'. 174 | 175 | Now we could bind all the 'getX' and 'getY' and 'move' member functions in the same way. However there is another way to expose these to Lua, in the form of properties. This allows you to use these as if they are public member variables, even though they are calling functions to set and get. 176 | 177 | ```c++ 178 | 179 | struct WidgetBinding : public ManualBind::Binding 180 | { 181 | static constexpr const char* class_name = "Widget"; 182 | 183 | static void create( lua_State* L ) 184 | { 185 | std::string newCaption( luaL_checkstring( L, 1 ) ); 186 | int nx = luaL_checkinteger( L, 2 ); 187 | int ny = luaL_checkinteger( L, 3 ); 188 | 189 | WidgetPtr w = std::make_shared( newCaption, nx, ny ); 190 | 191 | push( L, w ); 192 | 193 | return 1; 194 | } 195 | 196 | static luaL_Reg* members() 197 | { 198 | static luaL_Reg members[] = 199 | { 200 | { "show", show }, 201 | { nullptr, nullptr } 202 | }; 203 | return members; 204 | } 205 | 206 | static int show( lua_State* L ) 207 | { 208 | WidgetPtr w = fromStack( L, 1 ); 209 | // nil or not present will result in 'false'. Otherwise whatever lua_toboolean returns. 210 | bool upsideDown = luaL_opt( L, lua_toboolean, 2, false ); 211 | 212 | w->show( upsideDown ); 213 | 214 | return 0; 215 | } 216 | 217 | static bind_properties* properties() 218 | { 219 | static bind_properties properties[] = 220 | { 221 | { "x", get, set }, // Using the same two get and set functions for both 222 | { "y", get, set }, // However you can define seperate ones for each property. 223 | { nullptr, nullptr, nullptr } 224 | }; 225 | return properties; 226 | } 227 | 228 | static int get( lua_State* L ) 229 | { 230 | // Lua stack will be: 231 | // 1 - instance 232 | // 2 - property name to get. 233 | 234 | WidgetPtr w = fromStack( L, 1 ); 235 | const char* prop = luaL_checkstring( L, 2 ); 236 | 237 | switch( prop[0] ) 238 | { 239 | case 'x': 240 | lua_pushinteger( L, w->getX() ); 241 | break; 242 | case 'y': 243 | lua_pushinteger( L, w->getY() ); 244 | break; 245 | default: 246 | return luaL_error( L, "This should never happen." ); 247 | break; 248 | } 249 | 250 | return 1; // Returning one item on the stack. 251 | } 252 | 253 | static int set( lua_State* L ) 254 | { 255 | // Lua stack will be: 256 | // 1 - instance 257 | // 2 - property name to set. 258 | // 3 - value to set 259 | 260 | // In this example we can not set X or Y independantly, so we grab both first and update. 261 | WidgetPtr w = fromStack( L, 1 ); 262 | int x = w->getX(); 263 | int y = w->getY(); 264 | 265 | const char* prop = luaL_checkstring( L, 2 ); 266 | int value = luaL_checkinteger( L, 3 ); 267 | 268 | switch( prop[0] ) 269 | { 270 | case 'x': 271 | x = value; 272 | break; 273 | case 'y': 274 | y = value; 275 | break; 276 | default: 277 | return luaL_error( L, "this really can't happen!" ); 278 | break; 279 | } 280 | 281 | w->move( x, y ); 282 | 283 | return 0; 284 | } 285 | 286 | }; 287 | 288 | ``` 289 | 290 | There we have it. In this example both X and Y properties share the same glue code, but this is not required. They could have completely different functions each. The set function does some extra work to allow us to set X and Y independently in Lua, even though in C++ you need to set both at once. In this way your glue functions can make a class more Lua friendly, or just the way you want them. We can now do this in Lua. 291 | ```Lua 292 | widget1.x = 100 293 | widget1.y = widget.y + 5 294 | ``` 295 | Note the dot operator this time. 296 | 297 | This concludes the 'quick' tutorial. There are a few more things the binding code can do. Have a look at the examples and the tests. 298 | -------------------------------------------------------------------------------- /src/LuaRef.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | /* 3 | 4 | Copyright 2017, Nigel Atkinson 5 | 6 | License: The MIT License (http://www.opensource.org/licenses/mit-license.php) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | //============================================================================== 27 | 28 | #ifndef __LUAREF_H 29 | #define __LUAREF_H 30 | 31 | #include 32 | #include 33 | #include // For std::ignore 34 | #include "LuaStack.h" 35 | #include "LuaException.h" 36 | 37 | namespace ManualBind { 38 | 39 | struct LuaNil 40 | { 41 | }; 42 | 43 | template<> 44 | struct LuaStack 45 | { 46 | static inline void push( lua_State* L, LuaNil const& nil ) 47 | { 48 | lua_pushnil(L); 49 | } 50 | }; 51 | 52 | class LuaRef; 53 | 54 | class LuaRefBase 55 | { 56 | protected: 57 | lua_State* m_L; 58 | int m_ref; 59 | 60 | class StackPopper 61 | { 62 | lua_State* m_L; 63 | int m_count; 64 | public: 65 | StackPopper( lua_State* L, int count = 1 ) : m_L(L), m_count(count) 66 | {} 67 | ~StackPopper() 68 | { 69 | lua_pop( m_L, m_count ); 70 | } 71 | }; 72 | 73 | struct FromStack {}; 74 | 75 | // These constructors as destructor are protected as this 76 | // class should not be used directly. 77 | 78 | LuaRefBase( lua_State* L, FromStack ) : m_L( L ) 79 | { 80 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 81 | } 82 | 83 | LuaRefBase( lua_State* L, int ref ) : m_L( L ), m_ref( ref ) 84 | { 85 | } 86 | 87 | ~LuaRefBase() 88 | { 89 | luaL_unref( m_L, LUA_REGISTRYINDEX, m_ref ); 90 | } 91 | 92 | public: 93 | virtual void push() const 94 | { 95 | lua_rawgeti( m_L, LUA_REGISTRYINDEX, m_ref ); 96 | } 97 | 98 | std::string tostring() const 99 | { 100 | lua_getglobal( m_L, "tostring" ); 101 | push(); 102 | lua_call( m_L, 1, 1 ); 103 | const char* str = lua_tostring( m_L, 1 ); 104 | lua_pop( m_L, 1 ); 105 | return std::string(str); 106 | } 107 | 108 | int type () const 109 | { 110 | int result; 111 | push(); 112 | result = lua_type (m_L, -1); 113 | lua_pop (m_L, 1); 114 | return result; 115 | } 116 | 117 | inline bool isNil () const { 118 | return type () == LUA_TNIL; 119 | } 120 | inline bool isNumber () const { 121 | return type () == LUA_TNUMBER; 122 | } 123 | inline bool isString () const { 124 | return type () == LUA_TSTRING; 125 | } 126 | inline bool isTable () const { 127 | return type () == LUA_TTABLE; 128 | } 129 | inline bool isFunction () const { 130 | return type () == LUA_TFUNCTION; 131 | } 132 | inline bool isUserdata () const { 133 | return type () == LUA_TUSERDATA; 134 | } 135 | inline bool isThread () const { 136 | return type () == LUA_TTHREAD; 137 | } 138 | inline bool isLightUserdata () const { 139 | return type () == LUA_TLIGHTUSERDATA; 140 | } 141 | inline bool isBool() const { 142 | return type () == LUA_TBOOLEAN; 143 | } 144 | 145 | template 146 | inline LuaRef const operator()( Args... args ) const; 147 | 148 | template 149 | inline void call( int ret, Args... args ) const; 150 | 151 | template 152 | void append( T v ) const 153 | { 154 | push(); 155 | size_t len = lua_rawlen( m_L, -1); 156 | LuaStack::push( m_L, v ); 157 | lua_rawseti( m_L, -2, ++len ); 158 | lua_pop( m_L, 1 ); 159 | } 160 | 161 | template 162 | T cast() 163 | { 164 | StackPopper p(m_L); 165 | push(); 166 | return LuaStack::get( m_L, -1 ); 167 | } 168 | 169 | template 170 | operator T() 171 | { 172 | return cast(); 173 | } 174 | }; 175 | 176 | template 177 | class LuaTableElement : public LuaRefBase 178 | { 179 | friend class LuaRef; 180 | private: 181 | K m_key; 182 | 183 | // This constructor has to be public, so that the operator[] 184 | // with a differing template type can call it. 185 | // I could not find a way to 'friend' it. 186 | public: 187 | // Expects on the Lua stack 188 | // 1 - The table 189 | LuaTableElement( lua_State* L, K key ) 190 | : LuaRefBase( L, FromStack() ) 191 | , m_key( key ) 192 | { 193 | } 194 | 195 | void push() const override 196 | { 197 | lua_rawgeti( m_L, LUA_REGISTRYINDEX, m_ref ); 198 | LuaStack::push( m_L, m_key ); 199 | lua_gettable( m_L, -2 ); 200 | lua_remove( m_L, -2 ); 201 | } 202 | 203 | // Assign a new value to this table/key. 204 | template 205 | LuaTableElement& operator= ( T v ) 206 | { 207 | StackPopper p( m_L ); 208 | lua_rawgeti( m_L, LUA_REGISTRYINDEX, m_ref ); 209 | LuaStack::push( m_L, m_key ); 210 | LuaStack::push( m_L, v ); 211 | lua_settable( m_L, -3 ); 212 | return *this; 213 | } 214 | 215 | template 216 | LuaTableElement operator[]( NK key ) const 217 | { 218 | push(); 219 | return LuaTableElement( m_L, key ); 220 | } 221 | }; 222 | 223 | template 224 | struct LuaStack > 225 | { 226 | static inline void push( lua_State* L, LuaTableElement const& e ) 227 | { 228 | e.push(); 229 | } 230 | }; 231 | 232 | class LuaRef : public LuaRefBase 233 | { 234 | friend LuaRefBase; 235 | private: 236 | LuaRef( lua_State* L, FromStack fs ) : LuaRefBase( L, fs ) 237 | {} 238 | 239 | public: 240 | LuaRef( lua_State* L ) : LuaRefBase( L, LUA_REFNIL ) 241 | { 242 | } 243 | 244 | LuaRef( lua_State* L, const std::string& global ) : LuaRefBase( L, LUA_REFNIL ) 245 | { 246 | lua_getglobal( m_L, global.c_str() ); 247 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 248 | } 249 | 250 | LuaRef( LuaRef const& other ) : LuaRefBase( other.m_L, LUA_REFNIL ) 251 | { 252 | other.push(); 253 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 254 | } 255 | 256 | LuaRef( LuaRef&& other ) noexcept : LuaRefBase( other.m_L, other.m_ref ) 257 | { 258 | other.m_ref = LUA_REFNIL; 259 | } 260 | 261 | LuaRef& operator=( LuaRef&& other ) noexcept 262 | { 263 | if( this == &other ) return *this; 264 | 265 | std::swap( m_L, other.m_L); 266 | std::swap( m_ref, other.m_ref); 267 | 268 | return *this; 269 | } 270 | 271 | LuaRef& operator=( LuaRef const& other ) 272 | { 273 | if( this == &other ) return *this; 274 | luaL_unref( m_L, LUA_REGISTRYINDEX, m_ref ); 275 | other.push(); 276 | m_L = other.m_L; 277 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 278 | return *this; 279 | } 280 | 281 | template 282 | LuaRef& operator=( LuaTableElement&& other ) noexcept 283 | { 284 | luaL_unref( m_L, LUA_REGISTRYINDEX, m_ref ); 285 | other.push(); 286 | m_L = other.m_L; 287 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 288 | return *this; 289 | } 290 | 291 | template 292 | LuaRef& operator=( LuaTableElement const& other ) 293 | { 294 | luaL_unref( m_L, LUA_REGISTRYINDEX, m_ref ); 295 | other.push(); 296 | m_L = other.m_L; 297 | m_ref = luaL_ref( m_L, LUA_REGISTRYINDEX ); 298 | return *this; 299 | } 300 | 301 | template 302 | LuaTableElement operator[]( K key ) const 303 | { 304 | push(); 305 | return LuaTableElement( m_L, key ); 306 | } 307 | 308 | static LuaRef fromStack( lua_State* L, int index = -1 ) 309 | { 310 | lua_pushvalue( L, index ); 311 | return LuaRef( L, FromStack() ); 312 | } 313 | 314 | static LuaRef newTable (lua_State* L) 315 | { 316 | lua_newtable (L); 317 | return LuaRef (L, FromStack ()); 318 | } 319 | 320 | static LuaRef getGlobal (lua_State *L, char const* name) 321 | { 322 | lua_getglobal (L, name); 323 | return LuaRef (L, FromStack ()); 324 | } 325 | }; 326 | 327 | template<> 328 | struct LuaStack 329 | { 330 | static inline void push( lua_State* L, LuaRef const& r ) 331 | { 332 | r.push(); 333 | } 334 | }; 335 | 336 | template<> 337 | inline LuaRef const LuaRefBase::operator() () const 338 | { 339 | push(); 340 | LuaException::pcall (m_L, 0, 1); 341 | return LuaRef (m_L, FromStack ()); 342 | } 343 | 344 | template 345 | inline LuaRef const LuaRefBase::operator()( Args... args ) const 346 | { 347 | const int n = sizeof...(Args); 348 | push(); 349 | // Initializer expansion trick to call push for each arg. 350 | // https://stackoverflow.com/questions/25680461/variadic-template-pack-expansion 351 | int dummy[] = { 0, ( (void) LuaStack::push( m_L, std::forward(args) ), 0 ) ... }; 352 | std::ignore = dummy; 353 | LuaException::pcall( m_L, n, 1 ); 354 | return LuaRef (m_L, FromStack ()); 355 | } 356 | 357 | template<> 358 | inline void LuaRefBase::call( int ret ) const 359 | { 360 | push(); 361 | LuaException::pcall (m_L, 0, ret); 362 | return; // Return values, if any, are left on the Lua stack. 363 | } 364 | 365 | template 366 | inline void LuaRefBase::call( int ret, Args... args ) const 367 | { 368 | const int n = sizeof...(Args); 369 | push(); 370 | // Initializer expansion trick to call push for each arg. 371 | // https://stackoverflow.com/questions/25680461/variadic-template-pack-expansion 372 | int dummy[] = { 0, ( (void) LuaStack::push( m_L, std::forward(args) ), 0 ) ... }; 373 | std::ignore = dummy; 374 | LuaException::pcall( m_L, n, ret ); 375 | return; // Return values, if any, are left on the Lua stack. 376 | } 377 | 378 | template<> 379 | inline LuaRef LuaRefBase::cast() 380 | { 381 | push(); 382 | return LuaRef( m_L, FromStack() ); 383 | } 384 | 385 | }; // namespace ManualBind 386 | 387 | #endif // __LUAREF_H 388 | -------------------------------------------------------------------------------- /src/LuaStack.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | /* 3 | https://github.com/vinniefalco/LuaBridge 4 | 5 | Copyright 2012, Vinnie Falco 6 | Copyright 2007, Nathan Reed 7 | 8 | License: The MIT License (http://www.opensource.org/licenses/mit-license.php) 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | */ 28 | //============================================================================== 29 | 30 | #ifndef LUASTACK_H 31 | #define LUASTACK_H 32 | 33 | namespace ManualBind { 34 | 35 | template 36 | struct LuaStack; 37 | 38 | //------------------------------------------------------------------------------ 39 | /** 40 | Push an object onto the Lua stack. 41 | */ 42 | template 43 | inline void lua_push (lua_State* L, T t) 44 | { 45 | LuaStack ::push (L, t); 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | /** 50 | Receive the lua_State* as an argument. 51 | */ 52 | template <> 53 | struct LuaStack 54 | { 55 | static lua_State* get (lua_State* L, int) 56 | { 57 | return L; 58 | } 59 | }; 60 | 61 | //------------------------------------------------------------------------------ 62 | /** 63 | Push a lua_CFunction. 64 | */ 65 | template <> 66 | struct LuaStack 67 | { 68 | static void push (lua_State* L, lua_CFunction f) 69 | { 70 | lua_pushcfunction (L, f); 71 | } 72 | 73 | static lua_CFunction get (lua_State* L, int index) 74 | { 75 | return lua_tocfunction (L, index); 76 | } 77 | }; 78 | 79 | //------------------------------------------------------------------------------ 80 | /** 81 | LuaStack specialization for `int`. 82 | */ 83 | template <> 84 | struct LuaStack 85 | { 86 | static inline void push (lua_State* L, int value) 87 | { 88 | lua_pushinteger (L, static_cast (value)); 89 | } 90 | 91 | static inline int get (lua_State* L, int index) 92 | { 93 | return static_cast (luaL_checkinteger (L, index)); 94 | } 95 | }; 96 | 97 | template <> 98 | struct LuaStack 99 | { 100 | static inline void push (lua_State* L, int value) 101 | { 102 | lua_pushnumber (L, static_cast (value)); 103 | } 104 | 105 | static inline int get (lua_State* L, int index) 106 | { 107 | return static_cast (luaL_checknumber (L, index)); 108 | } 109 | }; 110 | //------------------------------------------------------------------------------ 111 | /** 112 | LuaStack specialization for `unsigned int`. 113 | */ 114 | template <> 115 | struct LuaStack 116 | { 117 | static inline void push (lua_State* L, unsigned int value) 118 | { 119 | lua_pushinteger (L, static_cast (value)); 120 | } 121 | 122 | static inline unsigned int get (lua_State* L, int index) 123 | { 124 | return static_cast (luaL_checkinteger (L, index)); 125 | } 126 | }; 127 | 128 | template <> 129 | struct LuaStack 130 | { 131 | static inline void push (lua_State* L, unsigned int value) 132 | { 133 | lua_pushnumber (L, static_cast (value)); 134 | } 135 | 136 | static inline unsigned int get (lua_State* L, int index) 137 | { 138 | return static_cast (luaL_checknumber (L, index)); 139 | } 140 | }; 141 | 142 | //------------------------------------------------------------------------------ 143 | /** 144 | LuaStack specialization for `unsigned char`. 145 | */ 146 | template <> 147 | struct LuaStack 148 | { 149 | static inline void push (lua_State* L, unsigned char value) 150 | { 151 | lua_pushinteger (L, static_cast (value)); 152 | } 153 | 154 | static inline unsigned char get (lua_State* L, int index) 155 | { 156 | return static_cast (luaL_checkinteger (L, index)); 157 | } 158 | }; 159 | 160 | template <> 161 | struct LuaStack 162 | { 163 | static inline void push (lua_State* L, unsigned char value) 164 | { 165 | lua_pushnumber (L, static_cast (value)); 166 | } 167 | 168 | static inline unsigned char get (lua_State* L, int index) 169 | { 170 | return static_cast (luaL_checknumber (L, index)); 171 | } 172 | }; 173 | 174 | //------------------------------------------------------------------------------ 175 | /** 176 | LuaStack specialization for `short`. 177 | */ 178 | template <> 179 | struct LuaStack 180 | { 181 | static inline void push (lua_State* L, short value) 182 | { 183 | lua_pushinteger (L, static_cast (value)); 184 | } 185 | 186 | static inline short get (lua_State* L, int index) 187 | { 188 | return static_cast (luaL_checkinteger (L, index)); 189 | } 190 | }; 191 | 192 | template <> 193 | struct LuaStack 194 | { 195 | static inline void push (lua_State* L, short value) 196 | { 197 | lua_pushnumber (L, static_cast (value)); 198 | } 199 | 200 | static inline short get (lua_State* L, int index) 201 | { 202 | return static_cast (luaL_checknumber (L, index)); 203 | } 204 | }; 205 | 206 | //------------------------------------------------------------------------------ 207 | /** 208 | LuaStack specialization for `unsigned short`. 209 | */ 210 | template <> 211 | struct LuaStack 212 | { 213 | static inline void push (lua_State* L, unsigned short value) 214 | { 215 | lua_pushinteger (L, static_cast (value)); 216 | } 217 | 218 | static inline unsigned short get (lua_State* L, int index) 219 | { 220 | return static_cast (luaL_checkinteger (L, index)); 221 | } 222 | }; 223 | 224 | template <> 225 | struct LuaStack 226 | { 227 | static inline void push (lua_State* L, unsigned short value) 228 | { 229 | lua_pushnumber (L, static_cast (value)); 230 | } 231 | 232 | static inline unsigned short get (lua_State* L, int index) 233 | { 234 | return static_cast (luaL_checknumber (L, index)); 235 | } 236 | }; 237 | 238 | //------------------------------------------------------------------------------ 239 | /** 240 | LuaStack specialization for `long`. 241 | */ 242 | template <> 243 | struct LuaStack 244 | { 245 | static inline void push (lua_State* L, long value) 246 | { 247 | lua_pushinteger (L, static_cast (value)); 248 | } 249 | 250 | static inline long get (lua_State* L, int index) 251 | { 252 | return static_cast (luaL_checkinteger (L, index)); 253 | } 254 | }; 255 | 256 | template <> 257 | struct LuaStack 258 | { 259 | static inline void push (lua_State* L, long value) 260 | { 261 | lua_pushnumber (L, static_cast (value)); 262 | } 263 | 264 | static inline long get (lua_State* L, int index) 265 | { 266 | return static_cast (luaL_checknumber (L, index)); 267 | } 268 | }; 269 | 270 | //------------------------------------------------------------------------------ 271 | /** 272 | LuaStack specialization for `unsigned long`. 273 | */ 274 | template <> 275 | struct LuaStack 276 | { 277 | static inline void push (lua_State* L, unsigned long value) 278 | { 279 | lua_pushinteger (L, static_cast (value)); 280 | } 281 | 282 | static inline unsigned long get (lua_State* L, int index) 283 | { 284 | return static_cast (luaL_checkinteger (L, index)); 285 | } 286 | }; 287 | 288 | template <> 289 | struct LuaStack 290 | { 291 | static inline void push (lua_State* L, unsigned long value) 292 | { 293 | lua_pushnumber (L, static_cast (value)); 294 | } 295 | 296 | static inline unsigned long get (lua_State* L, int index) 297 | { 298 | return static_cast (luaL_checknumber (L, index)); 299 | } 300 | }; 301 | 302 | //------------------------------------------------------------------------------ 303 | /** 304 | LuaStack specialization for `float`. 305 | */ 306 | template <> 307 | struct LuaStack 308 | { 309 | static inline void push (lua_State* L, float value) 310 | { 311 | lua_pushnumber (L, static_cast (value)); 312 | } 313 | 314 | static inline float get (lua_State* L, int index) 315 | { 316 | return static_cast (luaL_checknumber (L, index)); 317 | } 318 | }; 319 | 320 | template <> 321 | struct LuaStack 322 | { 323 | static inline void push (lua_State* L, float value) 324 | { 325 | lua_pushnumber (L, static_cast (value)); 326 | } 327 | 328 | static inline float get (lua_State* L, int index) 329 | { 330 | return static_cast (luaL_checknumber (L, index)); 331 | } 332 | }; 333 | 334 | //------------------------------------------------------------------------------ 335 | /** 336 | LuaStack specialization for `double`. 337 | */ 338 | template <> struct LuaStack 339 | { 340 | static inline void push (lua_State* L, double value) 341 | { 342 | lua_pushnumber (L, static_cast (value)); 343 | } 344 | 345 | static inline double get (lua_State* L, int index) 346 | { 347 | return static_cast (luaL_checknumber (L, index)); 348 | } 349 | }; 350 | 351 | template <> struct LuaStack 352 | { 353 | static inline void push (lua_State* L, double value) 354 | { 355 | lua_pushnumber (L, static_cast (value)); 356 | } 357 | 358 | static inline double get (lua_State* L, int index) 359 | { 360 | return static_cast (luaL_checknumber (L, index)); 361 | } 362 | }; 363 | 364 | //------------------------------------------------------------------------------ 365 | /** 366 | LuaStack specialization for `bool`. 367 | */ 368 | template <> 369 | struct LuaStack { 370 | static inline void push (lua_State* L, bool value) 371 | { 372 | lua_pushboolean (L, value ? 1 : 0); 373 | } 374 | 375 | static inline bool get (lua_State* L, int index) 376 | { 377 | return lua_toboolean (L, index) ? true : false; 378 | } 379 | }; 380 | 381 | template <> 382 | struct LuaStack { 383 | static inline void push (lua_State* L, bool value) 384 | { 385 | lua_pushboolean (L, value ? 1 : 0); 386 | } 387 | 388 | static inline bool get (lua_State* L, int index) 389 | { 390 | return lua_toboolean (L, index) ? true : false; 391 | } 392 | }; 393 | 394 | //------------------------------------------------------------------------------ 395 | /** 396 | LuaStack specialization for `char`. 397 | */ 398 | template <> 399 | struct LuaStack 400 | { 401 | static inline void push (lua_State* L, char value) 402 | { 403 | char str [2] = { value, 0 }; 404 | lua_pushstring (L, str); 405 | } 406 | 407 | static inline char get (lua_State* L, int index) 408 | { 409 | return luaL_checkstring (L, index) [0]; 410 | } 411 | }; 412 | 413 | template <> 414 | struct LuaStack 415 | { 416 | static inline void push (lua_State* L, char value) 417 | { 418 | char str [2] = { value, 0 }; 419 | lua_pushstring (L, str); 420 | } 421 | 422 | static inline char get (lua_State* L, int index) 423 | { 424 | return luaL_checkstring (L, index) [0]; 425 | } 426 | }; 427 | 428 | //------------------------------------------------------------------------------ 429 | /** 430 | LuaStack specialization for `float`. 431 | */ 432 | template <> 433 | struct LuaStack 434 | { 435 | static inline void push (lua_State* L, char const* str) 436 | { 437 | if (str != 0) 438 | lua_pushstring (L, str); 439 | else 440 | lua_pushnil (L); 441 | } 442 | 443 | static inline char const* get (lua_State* L, int index) 444 | { 445 | return lua_isnil (L, index) ? 0 : luaL_checkstring (L, index); 446 | } 447 | }; 448 | 449 | //------------------------------------------------------------------------------ 450 | /** 451 | LuaStack specialization for `std::string`. 452 | */ 453 | template <> 454 | struct LuaStack 455 | { 456 | static inline void push (lua_State* L, std::string const& str) 457 | { 458 | lua_pushlstring (L, str.c_str (), str.size()); 459 | } 460 | 461 | static inline std::string get (lua_State* L, int index) 462 | { 463 | size_t len; 464 | const char *str = luaL_checklstring(L, index, &len); 465 | return std::string (str, len); 466 | } 467 | }; 468 | 469 | //------------------------------------------------------------------------------ 470 | /** 471 | LuaStack specialization for `std::string const&`. 472 | */ 473 | template <> 474 | struct LuaStack 475 | { 476 | static inline void push (lua_State* L, std::string const& str) 477 | { 478 | lua_pushlstring (L, str.c_str(), str.size()); 479 | } 480 | 481 | static inline std::string get (lua_State* L, int index) 482 | { 483 | size_t len; 484 | const char *str = luaL_checklstring(L, index, &len); 485 | return std::string (str, len); 486 | } 487 | }; 488 | 489 | }; // namespace ManualBind 490 | 491 | #endif // LUASTACK_H 492 | -------------------------------------------------------------------------------- /src/LuaBinding.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | /* 3 | 4 | Copyright 2017, Nigel Atkinson 5 | 6 | License: The MIT License (http://www.opensource.org/licenses/mit-license.php) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | //============================================================================== 27 | 28 | /* Special members. 29 | * ---------------- 30 | * In addition to assigning a function to the likes of __tostring and other built 31 | * in Lua metamethods, three additional members can be used. 32 | * 33 | * __arrayindex This is called to provide a result when an object is indexed 34 | * with a number. E.g. a = obj[5] 35 | * 36 | * __arraynewindex This is called to store or otherwise use a value when assigned 37 | * to an object via a numerical index. E.g. obj[7] = a 38 | * 39 | * __upcast This will be called by LuaBindingUpCast to return a shared 40 | * pointer to a parent class, allowing for polymorphic use. 41 | * See upcast in the examples. 42 | * 43 | */ 44 | 45 | 46 | #ifndef BINDING_H 47 | #define BINDING_H 48 | #include 49 | #include 50 | #include "n4502.h" 51 | #include "LuaException.h" 52 | 53 | template< bool cond, typename U > 54 | using enable_if_t = typename std::enable_if< cond, U >::type; 55 | 56 | namespace ManualBind { 57 | 58 | // traits 59 | 60 | // constructor 61 | template 62 | using create_t = decltype(std::declval().create(std::declval())); 63 | template 64 | using has_create = detect; 65 | 66 | // members 67 | template 68 | using members_t = decltype(std::declval().members()); 69 | template 70 | using has_members = detect; 71 | 72 | // properties 73 | template 74 | using properties_t = decltype(std::declval().properties()); 75 | template 76 | using has_properties = detect; 77 | 78 | // any other stuff you might want to add to the metatable. 79 | template 80 | using extras_t = decltype(std::declval().setExtraMeta(std::declval())); 81 | template 82 | using has_extras = detect; 83 | 84 | // helper struct 85 | struct bind_properties { 86 | const char *name; 87 | lua_CFunction getter; 88 | lua_CFunction setter; 89 | }; 90 | 91 | // Called when Lua object is indexed: obj[ndx] 92 | int LuaBindingIndex( lua_State *L ); 93 | // Called whe Lua object index is assigned: obj[ndx] = blah 94 | int LuaBindingNewIndex( lua_State *L ); 95 | 96 | // Gets the table of extra values assigned to an instance. 97 | int LuaBindingGetExtraValuesTable( lua_State* L, int index ); 98 | 99 | void LuaBindingSetProperties( lua_State *L, bind_properties* properties ); 100 | 101 | // If the object at 'index' is a userdata with a metatable containing a __upcast 102 | // function, then replaces the userdata at 'index' in the stack with the result 103 | // of calling __upcast. 104 | // Otherwise the object at index is replaced with nil. 105 | int LuaBindingUpCast( lua_State* L, int index ); 106 | 107 | // Check the number of arguments are as expected. 108 | // Throw an error if not. 109 | void CheckArgCount( lua_State *L, int expected ); 110 | 111 | // B - the binding class / struct 112 | // T - the class you are binding to Lua. 113 | 114 | // Shared pointer version 115 | // Use this for classes that need to be shared between C++ and Lua, 116 | // or are expensive to copy. Think of it as like "by Reference". 117 | template 118 | struct Binding { 119 | 120 | // Push the object on to the Lua stack 121 | static void push( lua_State *L, const std::shared_ptr& sp ) 122 | { 123 | 124 | if( sp == nullptr ) { 125 | lua_pushnil( L ); 126 | return; 127 | } 128 | 129 | void *ud = lua_newuserdata( L, sizeof(std::shared_ptr)); 130 | 131 | new(ud) std::shared_ptr( sp ); 132 | 133 | luaL_setmetatable( L, B::class_name ); 134 | } 135 | 136 | static void setMembers( lua_State* L, std::true_type ) 137 | { 138 | luaL_setfuncs( L, B::members(), 0 ); 139 | } 140 | 141 | static void setMembers( lua_State* L, std::false_type ) 142 | { 143 | // Nada. 144 | } 145 | 146 | static void setProperties( lua_State* L, std::true_type ) 147 | { 148 | bind_properties* props = B::properties(); 149 | LuaBindingSetProperties( L, props ); 150 | } 151 | 152 | static void setProperties( lua_State* L, std::false_type ) 153 | { 154 | // Nada. 155 | } 156 | 157 | static void setExtras( lua_State* L, std::true_type ) 158 | { 159 | B::setExtraMeta( L ); 160 | } 161 | 162 | static void setExtras( lua_State*, std::false_type ) 163 | { 164 | // Nada. 165 | } 166 | 167 | static int construct( lua_State* L, std::true_type ) 168 | { 169 | // Remove table from stack. 170 | lua_remove( L, 1 ); 171 | // Call create. 172 | return B::create( L ); 173 | } 174 | 175 | static int construct( lua_State* L, std::false_type ) 176 | { 177 | return luaL_error( L, "Can not create an instance of %s.", B::class_name); 178 | } 179 | 180 | static int construct( lua_State* L ) 181 | { 182 | has_create createTrait; 183 | return construct( L, createTrait); 184 | } 185 | 186 | static int pairs( lua_State* L ) 187 | { 188 | lua_getglobal( L, "pairs" ); 189 | luaL_getmetatable( L, B::class_name ); 190 | lua_pcall( L, 1, LUA_MULTRET, 0 ); 191 | return 3; 192 | } 193 | 194 | // Create metatable and register Lua constructor 195 | static void register_class( lua_State *L ) 196 | { 197 | has_members membersTrait; 198 | has_properties propTrait; 199 | has_extras extrasTrait; 200 | 201 | lua_newtable( L ); // Class access 202 | lua_newtable( L ); // Class access metatable 203 | 204 | luaL_newmetatable( L, B::class_name ); 205 | setMembers( L, membersTrait ); 206 | lua_pushcfunction( L, LuaBindingIndex ); 207 | lua_setfield( L, -2, "__index" ); 208 | lua_pushcfunction( L, LuaBindingNewIndex ); 209 | lua_setfield( L, -2, "__newindex" ); 210 | lua_pushcfunction( L, destroy ); 211 | lua_setfield( L, -2, "__gc" ); 212 | lua_pushcfunction( L, close ); 213 | lua_setfield( L, -2, "__close" ); 214 | lua_newtable( L ); // __properties 215 | setProperties( L, propTrait ); 216 | lua_setfield( L, -2, "__properties" ); 217 | setExtras( L, extrasTrait ); 218 | lua_pushcfunction( L, pairs ); 219 | lua_setfield( L, -2, "__pairs" ); 220 | 221 | lua_setfield( L, -2, "__index" ); // Set metatable as index table. 222 | 223 | lua_pushcfunction( L, construct ); 224 | lua_setfield( L, -2, "__call" ); 225 | 226 | lua_pushcfunction( L, pairs ); 227 | lua_setfield( L, -2, "__pairs" ); 228 | 229 | lua_setmetatable( L, -2 ); 230 | lua_setglobal( L, B::class_name ); 231 | } 232 | 233 | // Called when Lua object is garbage collected. 234 | static int destroy( lua_State *L ) 235 | { 236 | void* ud = luaL_checkudata( L, 1, B::class_name ); 237 | 238 | auto sp = static_cast*>(ud); 239 | 240 | // Explicitly called, as this was 'placement new'd 241 | sp->~shared_ptr(); 242 | 243 | return 0; 244 | } 245 | 246 | // Called when Lua object goes out of scope with the annotation 247 | static int close( lua_State *L ) 248 | { 249 | void* ud = luaL_checkudata( L, 1, B::class_name ); 250 | 251 | auto sp = static_cast*>(ud); 252 | 253 | sp->reset(); 254 | 255 | return 0; 256 | } 257 | 258 | // Grab object shared pointer from the Lua stack 259 | static const std::shared_ptr& fromStack( lua_State *L, int index ) 260 | { 261 | void* ud = luaL_checkudata( L, index, B::class_name ); 262 | 263 | auto sp = static_cast*>(ud); 264 | 265 | return *sp; 266 | } 267 | 268 | static const std::shared_ptr& fromStackThrow( lua_State *L, int index ) 269 | { 270 | void* ud = luaL_testudata( L, index, B::class_name ); 271 | 272 | if( ud == nullptr ) 273 | throw LuaException( "Unexpected item on Lua stack." ); 274 | 275 | auto sp = static_cast*>(ud); 276 | 277 | return *sp; 278 | } 279 | 280 | static bool isType( lua_State *L, int index) 281 | { 282 | return luaL_testudata( L, index, B::class_name) != nullptr; 283 | } 284 | 285 | }; 286 | 287 | // Plain Old Data POD version. 288 | // Use this for simpler classes/structures where coping is fairly cheap, and 289 | // C++ and Lua do not need to operate on the same instance. 290 | // Think of this as "by Value" 291 | template 292 | struct PODBinding { 293 | 294 | // Push the object on to the Lua stack 295 | static void push( lua_State *L, const T& value ) 296 | { 297 | void *ud = lua_newuserdata( L, sizeof(T)); 298 | 299 | new(ud) T( value ); 300 | 301 | luaL_setmetatable( L, B::class_name ); 302 | } 303 | 304 | static void setMembers( lua_State* L, std::true_type ) 305 | { 306 | luaL_setfuncs( L, B::members(), 0 ); 307 | } 308 | 309 | static void setMembers( lua_State*, std::false_type ) 310 | { 311 | // Nada. 312 | } 313 | 314 | static void setProperties( lua_State* L, std::true_type ) 315 | { 316 | bind_properties* props = B::properties(); 317 | LuaBindingSetProperties( L, props ); 318 | } 319 | 320 | static void setProperties( lua_State*, std::false_type ) 321 | { 322 | // Nada. 323 | } 324 | 325 | static void setExtras( lua_State* L, std::true_type ) 326 | { 327 | B::setExtraMeta( L ); 328 | } 329 | 330 | static void setExtras( lua_State*, std::false_type ) 331 | { 332 | // Nada. 333 | } 334 | 335 | static int construct( lua_State* L, std::true_type ) 336 | { 337 | // Remove table from stack. 338 | lua_remove( L, 1 ); 339 | // Call create. 340 | return B::create( L ); 341 | } 342 | 343 | static int construct( lua_State* L, std::false_type ) 344 | { 345 | return luaL_error( L, "Can not create an instance of %s.", B::class_name); 346 | } 347 | 348 | static int construct( lua_State* L ) 349 | { 350 | has_create createTrait; 351 | return construct( L, createTrait); 352 | } 353 | 354 | static int pairs( lua_State* L ) 355 | { 356 | lua_getglobal( L, "pairs" ); 357 | luaL_getmetatable( L, B::class_name ); 358 | lua_pcall( L, 1, LUA_MULTRET, 0 ); 359 | return 3; 360 | } 361 | 362 | // Create metatable and register Lua constructor 363 | static void register_class( lua_State *L ) 364 | { 365 | has_members membersTrait; 366 | has_properties propTrait; 367 | has_extras extrasTrait; 368 | 369 | lua_newtable( L ); // Class access 370 | lua_newtable( L ); // Class access metatable 371 | 372 | luaL_newmetatable( L, B::class_name ); 373 | setMembers( L, membersTrait ); 374 | lua_pushcfunction( L, LuaBindingIndex ); 375 | lua_setfield( L, -2, "__index" ); 376 | lua_pushcfunction( L, LuaBindingNewIndex ); 377 | lua_setfield( L, -2, "__newindex" ); 378 | //lua_pushcfunction( L, destroy ); -- if you need to destruct a POD 379 | //lua_setfield( L, -2, "__gc" ); -- set __gc to destory in the members. 380 | lua_newtable( L ); // __properties 381 | setProperties( L, propTrait ); 382 | lua_setfield( L, -2, "__properties" ); 383 | setExtras( L, extrasTrait ); 384 | 385 | lua_setfield( L, -2, "__index" ); 386 | 387 | lua_pushcfunction( L, construct ); 388 | lua_setfield( L, -2, "__call" ); 389 | 390 | lua_pushcfunction( L, pairs ); 391 | lua_setfield( L, -2, "__pairs" ); 392 | 393 | lua_setmetatable( L, -2 ); 394 | lua_setglobal( L, B::class_name ); 395 | } 396 | 397 | // This is still here should a POD really need 398 | // destructing. Shouldn't be a common case. 399 | static int destroy( lua_State *L ) 400 | { 401 | void* ud = luaL_checkudata( L, 1, B::class_name ); 402 | 403 | auto p = static_cast(ud); 404 | 405 | // Explicitly called, as this was 'placement new'd 406 | p->~T(); 407 | 408 | return 0; 409 | } 410 | 411 | // Grab object pointer from the Lua stack 412 | static T& fromStack( lua_State *L, int index ) 413 | { 414 | void* ud = luaL_checkudata( L, index, B::class_name ); 415 | 416 | auto p = static_cast(ud); 417 | 418 | return *p; 419 | } 420 | 421 | static T& fromStackThrow( lua_State *L, int index ) 422 | { 423 | void* ud = luaL_testudata( L, index, B::class_name ); 424 | 425 | if( ud == nullptr ) 426 | throw LuaException( "Unexpected item on Lua stack." ); 427 | 428 | auto p = static_cast(ud); 429 | 430 | return *p; 431 | } 432 | 433 | static bool isType( lua_State *L, int index) 434 | { 435 | return luaL_testudata( L, index, B::class_name) != nullptr; 436 | } 437 | 438 | }; 439 | 440 | 441 | }; // namespace ManualBind 442 | 443 | #endif // BINDING_H 444 | --------------------------------------------------------------------------------