diff --git a/CMakeLists.txt b/CMakeLists.txt index e5c1057b..95368996 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,12 +112,12 @@ include(CMakePackageConfigHelpers) set(LUA_VERSION "5.3.4" CACHE STRING "The version of Lua needed. Can be 5.1, 5.2, 5.3, LuaJIT, or a more specific 3-part version number for a specifc Lua (e.g., 5.3.4 or luajit-2.0.5)") set(BUILD_LUA TRUE CACHE BOOL "Always build Lua, do not search for it in the system") set(PLATFORM "x64" CACHE STRING "Target platform to compile for when building binaries (x86, x64)") -option(CI "Enable build of tests" OFF) +option(CI "Whether or not we are in continguous integration mode" OFF) option(TESTS "Enable build of tests" OFF) option(EXAMPLES "Enable build of examples" OFF) option(INTEROP_EXAMPLES "Enable build of interop examples" OFF) option(DYNAMIC_LOADING_EXAMPLES "Enable build of interop examples" OFF) -option(SINGLE "Enable build of single header files" ON) +option(SINGLE "Enable build of single header files" OFF) option(DOCS "Enable build of documentation" OFF) # Single tests and examples tests will only be turned on if both SINGLE and TESTS are defined CMAKE_DEPENDENT_OPTION(TESTS_SINGLE "Enable build of tests using the generated single headers" ON diff --git a/cmake/Modules/FindLuaBuild/LuaJIT.cmake b/cmake/Modules/FindLuaBuild/LuaJIT.cmake index b58f97ff..f29a3a22 100644 --- a/cmake/Modules/FindLuaBuild/LuaJIT.cmake +++ b/cmake/Modules/FindLuaBuild/LuaJIT.cmake @@ -212,7 +212,7 @@ else () set(LUA_JIT_PREBUILT_EXP ${LUA_JIT_LIB_EXP_FILENAME}) if (WIN32) - list(APPEND LUA_JIT_MAKE_BUILD_MODIFICATIONS "HOST_SYS=Windows" "TARGET_SYS=Windows") + list(APPEND LUA_JIT_MAKE_BUILD_MODIFICATIONS "HOST_SYS=Windows" "TARGET_SYS=Windows" "TARGET_AR=ar rcus") endif() list(APPEND LUA_JIT_MAKE_BUILD_MODIFICATIONS "CFLAGS=${LUA_JIT_MAKE_CFLAGS_MODIFICATIONS}") list(APPEND LUA_JIT_MAKE_BUILD_MODIFICATIONS "TARGET_CFLAGS=${LUA_JIT_MAKE_TARGET_CFLAGS_MODIFICATIONS}") diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index d388a5d0..7d1987dc 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -19,7 +19,6 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo environment this_environment proxy - containers as_container nested as_table diff --git a/docs/source/api/as_args.rst b/docs/source/api/as_args.rst index eb93a2e2..9e158dad 100644 --- a/docs/source/api/as_args.rst +++ b/docs/source/api/as_args.rst @@ -14,37 +14,10 @@ as_args ``sol::as_args`` is a function that that takes an iterable and turns it into multiple arguments to a function call. It forwards its arguments, and is meant to be used as shown below: -.. code-block:: cpp + +.. literalinclude:: ../../../examples/args_from_container.cpp + :caption: args_from_container.cpp :linenos: - :caption: as_args.c++ - - #define SOL_CHECK_ARGUMENTS - #include - - #include - #include - - int main(int argc, const char* argv[]) { - - sol::state lua; - lua.open_libraries(); - - lua.script("function f (a, b, c, d) print(a, b, c, d) end"); - - sol::function f = lua["f"]; - - std::vector v2{ 3, 4 }; - f(1, 2, sol::as_args(v2)); - - std::set v4{ 3, 1, 2, 4 }; - f(sol::as_args(v4)); - - int v3[] = { 2, 3, 4 }; - f(1, sol::as_args(v3)); - - return 0; - } - It is basically implemented as a `one-way customization point`_. For more information about customization points, see the :doc:`tutorial on how to customize Sol to work with your types<../tutorial/customization>`. diff --git a/docs/source/api/as_function.rst b/docs/source/api/as_function.rst index e7be9364..13afc36f 100644 --- a/docs/source/api/as_function.rst +++ b/docs/source/api/as_function.rst @@ -12,56 +12,13 @@ This function serves the purpose of ensuring that a callable struct (like a lamb This class can also make it so usertypes bind variable types as functions to for usertype bindings. -.. code-block:: cpp - - #include - - int main () { - struct callable { - int operator()( int a, bool b ) { - return a + b ? 10 : 20; - } - }; +.. literalinclude:: ../../../examples/docs/as_function.cpp + :linenos: - sol::state lua; - // Binds struct as userdata - lua.set( "not_func", callable() ); - // Binds struct as function - lua.set( "func", sol::as_function( callable() ) ); - // equivalent: lua.set_function( "func", callable() ); - // equivalent: lua["func"] = callable(); - } - -Note that if you actually want a userdata, but you want it to be callable, you simply need to create a :ref:`sol::table::new_usertype` and then bind the ``"__call"`` metamethod (or just use ``sol::meta_function::call`` :ref:`enumeration`). +Note that if you actually want a userdata, but you want it to be callable, you simply need to create a :ref:`sol::table::new_usertype` and then bind the ``"__call"`` metamethod (or just use ``sol::meta_function::call`` :ref:`enumeration`). This may or may not be done automatically for you, depending on whether or not the call operator is overloaded and such. Here's an example of binding a variable as a function to a usertype: -.. code-block:: cpp - - #include - - int main () { - class B { - public: - int bvar = 24; - }; - - sol::state lua; - lua.open_libraries(); - lua.new_usertype("B", - // bind as variable - "b", &B::bvar, - // bind as function - "f", sol::as_function(&B::bvar) - ); - - B b; - lua.set("b", &b); - lua.script("x = b:f()"); - lua.script("y = b.b"); - int x = lua["x"]; - int y = lua["y"]; - assert(x == 24); - assert(y == 24); - } +.. literalinclude:: ../../../examples/docs/as_function_usertype_member_variable.cpp + :linenos: diff --git a/docs/source/api/as_returns.rst b/docs/source/api/as_returns.rst index a4693dea..8f38fee5 100644 --- a/docs/source/api/as_returns.rst +++ b/docs/source/api/as_returns.rst @@ -15,36 +15,5 @@ as_returns This allows you to wrap up a source that has ``begin`` and ``end`` iterator-returning functions on it and return it as multiple results into Lua. To have more control over the returns, use :doc:`sol::variadic_results`. -.. code-block:: cpp +.. literalinclude:: ../../../examples/as_returns.cpp :linenos: - :caption: as_returns.c++ - - #define SOL_CHECK_ARGUMENTS - #include - - #include - #include - - int main () { - sol::state lua; - - lua.set_function("f", []() { - std::set results{ "arf", "bark", "woof" }; - return sol::as_returns(std::move(results)); - }); - - lua.script(R"( - v1, v2, v3 = f() - )"); - }()); - - std::string v1 = lua["v1"]; - std::string v2 = lua["v2"]; - std::string v3 = lua["v3"]; - - assert(v1 == "arf"); - assert(v2 == "bark"); - assert(v3 == "woof"); - - return 0; - } diff --git a/docs/source/api/as_table.rst b/docs/source/api/as_table.rst index b095a417..2b9ed510 100644 --- a/docs/source/api/as_table.rst +++ b/docs/source/api/as_table.rst @@ -13,13 +13,8 @@ as_table This function serves the purpose of ensuring that an object is pushed -- if possible -- like a table into Lua. The container passed here can be a pointer, a reference, a ``std::reference_wrapper`` around a container, or just a plain container value. It must have a begin/end function, and if it has a ``std::pair`` as its ``value_type``, it will be pushed as a dictionary. Otherwise, it's pushed as a sequence. -.. code-block:: cpp - - sol::state lua; - lua.open_libraries(); - lua.set("my_table", sol::as_table(std::vector{ 1, 2, 3, 4, 5 })); - lua.script("for k, v in ipairs(my_table) do print(k, v) assert(k == v) end"); - +.. literalinclude:: ../../../examples/docs/as_table_ipairs.cpp + :linenos: Note that any caveats with Lua tables apply the moment it is serialized, and the data cannot be gotten out back out in C++ as a C++ type. You can deserialize the Lua table into something explicitly using the ``sol::as_table_t`` marker for your get and conversion operations using Sol. At that point, the returned type is deserialized **from** a table, meaning you cannot reference any kind of C++ data directly as you do with regular userdata/usertypes. *All C++ type information is lost upon serialization into Lua.* diff --git a/docs/source/api/c_call.rst b/docs/source/api/c_call.rst index 5c1e3ba0..cc637b99 100644 --- a/docs/source/api/c_call.rst +++ b/docs/source/api/c_call.rst @@ -11,7 +11,11 @@ c_call template int c_call (lua_State* L); -The goal of ``sol::c_call<...>`` is to provide a way to wrap a function and transport it through a compile-time context. This enables faster speed at the cost of a much harder to read / poorer interface, and can alleviate some template compilation speed issues. ``sol::c_call`` expects a type for its first template argument, and a value of the previously provided type for the second template argument. To make a compile-time transported overloaded function, specify multiple functions in the same ``type, value`` pairing, but put it inside of a ``sol::wrap``. Note that is can also be placed into the argument list for a :doc:`usertype` as well. +The goal of ``sol::c_call<...>`` is to provide a way to wrap a function and transport it through a compile-time context. This enables faster speed at the cost of a much harder to read / poorer interface, and can alleviate some template compilation speed issues. ``sol::c_call`` expects a type for its first template argument, and a value of the previously provided type for the second template argument. To make a compile-time transported overloaded function, specify multiple functions in the same ``type, value`` pairing, but put it inside of a ``sol::wrap``. + +.. note:: + + This can also be placed into the argument list for a :doc:`usertype` as well. This pushes a raw ``lua_CFunction`` into whatever you pass the resulting ``c_call`` function pointer into, whether it be a table or a userdata or whatever else using sol2's API. The resulting ``lua_CFunction`` can also be used directly with the lua API, just like many of sol2's types can be intermingled with Lua's API if you know what you're doing. @@ -19,57 +23,7 @@ It is advisable for the user to consider making a macro to do the necessary ``de Here's an example below of various ways to use ``sol::c_call``: -.. code-block:: cpp +.. literalinclude:: ../../../examples/c_call.cpp :linenos: - :caption: Compile-time transported function calls - - #include "sol.hpp" - - int f1(int) { return 32; } - - int f2(int, int) { return 1; } - - struct fer { - double f3(int, int) { - return 2.5; - } - }; - - - int main() { - - sol::state lua; - // overloaded function f - lua.set("f", sol::c_call, sol::wrap, sol::wrap>); - // singly-wrapped function - lua.set("g", sol::c_call>); - // without the 'sol::wrap' boilerplate - lua.set("h", sol::c_call); - // object used for the 'fer' member function call - lua.set("obj", fer()); - - // call them like any other bound function - lua.script("r1 = f(1)"); - lua.script("r2 = f(1, 2)"); - lua.script("r3 = f(obj, 1, 2)"); - lua.script("r4 = g(1)"); - lua.script("r5 = h(1, 2)"); - - // get the results and see - // if it worked out - int r1 = lua["r1"]; - // r1 == 32 - int r2 = lua["r2"]; - // r2 == 1 - double r3 = lua["r3"]; - // r3 == 2.5 - int r4 = lua["r4"]; - // r4 == 32 - int r5 = lua["r5"]; - // r5 == 1 - - return 0; - } - .. _one similar to this: http://stackoverflow.com/a/5628222/5280922 diff --git a/docs/source/api/containers.rst b/docs/source/api/containers.rst deleted file mode 100644 index 19d35b00..00000000 --- a/docs/source/api/containers.rst +++ /dev/null @@ -1,6 +0,0 @@ -containers -========== -*for handling ``std::vector/map/set`` and others* - - -See the full :doc:`containers documentation at the top level container page<../containers>` diff --git a/docs/source/api/environment.rst b/docs/source/api/environment.rst index b63c3206..d8bcb33c 100644 --- a/docs/source/api/environment.rst +++ b/docs/source/api/environment.rst @@ -18,39 +18,8 @@ This type is passed to :ref:`sol::state(_view)::script/do_x - - int main (int, char*[]) { - sol::state lua; - lua.open_libraries(); - sol::environment my_env(lua, sol::create); - // set value, and we need to explicitly allow for - // access to "print", since a new environment hides - // everything that's not defined inside of it - // NOTE: hiding also hides library functions (!!) - // BE WARNED - my_env["var"] = 50; - my_env["print"] = lua["print"]; - - sol::environment my_other_env(lua, sol::create, lua.globals()); - // do not need to explicitly allow access to "print", - // since we used the "Set a fallback" version - // of the sol::environment constructor - my_other_env["var"] = 443; - - // output: 50 - lua.script("print(var)", my_env); - - // output: 443 - lua.script("print(var)", my_other_env); - - return 0; - } - +.. literalinclude:: ../../../examples/docs/preparing_environments.cpp + :linenos: Also note that ``sol::environment`` derives from ``sol::table``, which also derives from ``sol::reference``: in other words, copying one ``sol::environment`` value to another ``sol::environment`` value **does not** deep-copy the table, just creates a new reference pointing to the same lua object. diff --git a/docs/source/api/error.rst b/docs/source/api/error.rst index cc32efc4..ab8b1999 100644 --- a/docs/source/api/error.rst +++ b/docs/source/api/error.rst @@ -18,4 +18,4 @@ error If an eror is thrown by Sol, it is going to be of this type. We use this in a single place: the default ``at_panic`` function we bind on construction of a :ref:`sol::state`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are nil unless you specifically use it to pull errors out of things such as :doc:`sol::protected_function`. -As it derives from ``std::runtime_error``, which derives from ``std::exception``, you can catch it with a ``catch (const std::exception& )`` clause in your try/catch blocks. You can retrieve a string error from Lua (Lua pushes all its errors as string returns) by using this type with any of the get or lookup functions in Sol. \ No newline at end of file +As it derives from ``std::runtime_error``, which derives from ``std::exception``, you can catch it with a ``catch (const std::exception& )`` clause in your try/catch blocks. You can retrieve a string error from Lua (Lua pushes all its errors as string returns) by using this type with any of the get or lookup functions in Sol. diff --git a/docs/source/api/function.rst b/docs/source/api/function.rst index 0464caa1..bfe0e2a7 100644 --- a/docs/source/api/function.rst +++ b/docs/source/api/function.rst @@ -22,45 +22,25 @@ Function is a correct-assuming version of :doc:`protected_function`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack. You can also return multiple values by using ``std::tuple``, or if you need to bind them to pre-existing variables use ``sol::tie``: -.. code-block:: cpp +.. literalinclude:: ../../../examples/tie.cpp + :lines: 24- :linenos: - sol::state lua; - - lua.script( "function f () return 10, 11, 12 end" ); - - sol::function f = lua["f"]; - std::tuple abc = f(); // 10, 11, 12 from Lua - // or - int a, b, c; - sol::tie(a, b, c) = f(); // a = 10, b = 11, c = 12 from Lua - This makes it much easier to work with multiple return values. Using ``std::tie`` from the C++ standard will result in dangling references or bad behavior because of the very poor way in which C++ tuples/``std::tie`` were specified and implemented: please use ``sol::tie( ... )`` instead to satisfy any multi-return needs. .. _function-result-warning: diff --git a/docs/source/api/metatable_key.rst b/docs/source/api/metatable_key.rst index 19367b01..b2d40e0f 100644 --- a/docs/source/api/metatable_key.rst +++ b/docs/source/api/metatable_key.rst @@ -10,145 +10,6 @@ metatable_key You can use this in conjunction with :doc:`sol::table` to set/get a metatable. Lua metatables are powerful ways to override default behavior of objects for various kinds of operators, among other things. Here is an entirely complete example, showing getting and working with a :doc:`usertype`'s metatable defined by Sol: -.. code-block:: cpp +.. literalinclude:: ../../../examples/metatable_key_low_level.cpp :caption: messing with metatables :linenos: - - #include - - int main () { - - struct bark { - int operator()(int x) { - return x; - } - }; - - sol::state lua; - lua.open_libraries(sol::lib::base); - - lua.new_usertype("bark", - sol::meta_function::call_function, &bark::operator() - ); - - bark b; - lua.set("b", &b); - - sol::table b_as_table = lua["b"]; - sol::table b_metatable = b_as_table[sol::metatable_key]; - sol::function b_call = b_metatable["__call"]; - sol::function b_as_function = lua["b"]; - - int result1 = b_as_function(1); - int result2 = b_call(b, 1); - // result1 == result2 == 1 - } - -It's further possible to have a "Dynamic Getter" (`thanks to billw2012 and Nava2 for this example`_!): - -.. code-block:: cpp - :caption: One way to make dynamic properties (there are others!) - :linenos: - - #include - #include - - struct PropertySet { - sol::object get_property_lua(const char* name, sol::this_state s) - { - auto& var = props[name]; - return sol::make_object(s, var); - } - - void set_property_lua(const char* name, sol::stack_object object) - { - props[name] = object.as(); - } - - std::unordered_map props; - }; - - struct DynamicObject { - PropertySet& get_dynamic_props() { - return dynamic_props; - } - - PropertySet dynamic_props; - }; - - - int main () { - sol::state lua; - lua.open_libraries(sol::lib::base); - - lua.new_usertype("PropertySet", - sol::meta_function::new_index, &PropertySet::set_property_lua, - sol::meta_function::index, &PropertySet::get_property_lua - ); - - lua.new_usertype("DynamicObject", - "props", sol::property(&DynamicObject::get_dynamic_props) - ); - - lua.script(R"( - obj = DynamicObject:new() - obj.props.name = 'test name' - print('name = ' .. obj.props.name) - )"); - - std::string name = lua["obj"]["props"]["name"]; - // name == "test name"; - } - - -You can even manipulate the ability to set and get items using metatable objects on a usertype or similar: - -.. code-block:: cpp - :caption: messing with metatables - vector type - :linenos: - - #include - - class vector { - public: - double data[3]; - - vector() : data{ 0,0,0 } {} - - double& operator[](int i) - { - return data[i]; - } - - - static double my_index(vector& v, int i) - { - return v[i]; - } - - static void my_new_index(vector& v, int i, double x) - { - v[i] = x; - } - }; - - int main () { - sol::state lua; - lua.open_libraries(sol::lib::base); - lua.new_usertype("vector", sol::constructors>(), - sol::meta_function::index, &vector::my_index, - sol::meta_function::new_index, &vector::my_new_index); - lua.script("v = vector.new()\n" - "print(v[1])\n" - "v[2] = 3\n" - "print(v[2])\n" - ); - - vector& v = lua["v"]; - // v[0] == 0.0; - // v[1] == 0.0; - // v[2] == 3.0; - } - - -.. _thanks to billw2012 and Nava2 for this example: https://github.com/ThePhD/sol2/issues/71#issuecomment-225402055 \ No newline at end of file diff --git a/docs/source/api/nested.rst b/docs/source/api/nested.rst index aa9ed050..84852343 100644 --- a/docs/source/api/nested.rst +++ b/docs/source/api/nested.rst @@ -10,7 +10,7 @@ nested }; -``sol::nested<...>`` is a template class similar to :doc:`sol::as_table`, but with the caveat that every :doc:`container type` within the ``sol::nested`` type will be retrieved as a table from lua. This is helpful when you need to receive C++-style vectors, lists, and maps nested within each other: all of them will be deserialized from lua using table properties rather than anything else. +``sol::nested<...>`` is a template class similar to :doc:`sol::as_table`, but with the caveat that every :doc:`container type<../containers>` within the ``sol::nested`` type will be retrieved as a table from lua. This is helpful when you need to receive C++-style vectors, lists, and maps nested within each other: all of them will be deserialized from lua using table properties rather than anything else. Note that any caveats with Lua tables apply the moment it is serialized, and the data cannot be gotten out back out in C++ as a C++ type. You can deserialize the Lua table into something explicitly using the ``sol::as_table_t`` marker for your get and conversion operations using Sol. At that point, the returned type is deserialized **from** a table, meaning you cannot reference any kind of C++ data directly as you do with regular userdata/usertypes. *All C++ type information is lost upon serialization into Lua.* diff --git a/docs/source/api/new_table.rst b/docs/source/api/new_table.rst index 2eb1ffb9..eb136127 100644 --- a/docs/source/api/new_table.rst +++ b/docs/source/api/new_table.rst @@ -7,15 +7,10 @@ new_table struct new_table; - const new_table create = new_table{}; + constexpr const new_table create = new_table{}; ``sol::new_table`` serves the purpose of letting you create tables using the constructor of :doc:`sol::table
` and :doc:`sol::environment`. It also disambiguates the other kinds of constructors, so is **necessary** to be specified. Leaving it off will result in the wrong constructor to be called, for either ``sol::table`` or ``sol::environment``. -There are two kinds of tables: the global table and non-global tables: however, both have the exact same interface and all ``sol::global_table`` s are convertible to regular ``sol::table`` s. - -Tables are the core of Lua, and they are very much the core of Sol. - - members ------- diff --git a/docs/source/api/overload.rst b/docs/source/api/overload.rst index 770ff321..5fc64a09 100644 --- a/docs/source/api/overload.rst +++ b/docs/source/api/overload.rst @@ -29,63 +29,21 @@ Its use is simple: wherever you can pass a function type to Lua, whether its on The functions can be any kind of function / function object (lambda). Given these functions and struct: -.. code-block:: cpp +.. literalinclude:: ../../../examples/overloading_with_members.cpp :linenos: - - struct pup { - int barks = 0; - - void bark () { - ++barks; // bark! - } - - bool is_cute () const { - return true; - } - }; - - void ultra_bark( pup& p, int barks) { - for (; barks --> 0;) p.bark(); - } - - void picky_bark( pup& p, std::string s) { - if ( s == "bark" ) - p.bark(); - } + :lines: 1-27 You then use it just like you would for any other part of the api: -.. code-block:: cpp +.. literalinclude:: ../../../examples/overloading_with_members.cpp :linenos: + :lines: 29-45 - sol::state lua; +Doing the following in Lua will call the specific overloads chosen, and their associated functions: - lua.set_function( "bark", sol::overload( - ultra_bark, - []() { return "the bark from nowhere"; } - ) ); - - lua.new_usertype( "pup", - // regular function - "is_cute", &pup::is_cute, - // overloaded function - "bark", sol::overload( &pup::bark, &picky_bark ) - ); - -Thusly, doing the following in Lua: - -.. code-block:: Lua - :caption: pup.lua +.. literalinclude:: ../../../examples/overloading_with_members.cpp :linenos: - - local barker = pup.new() - pup:bark() -- calls member function pup::bark - pup:bark(20) -- calls ultra_bark - pup:bark("meow") -- picky_bark, no bark - pup:bark("bark") -- picky_bark, bark - - bark(pup, 20) -- calls ultra_bark - local nowherebark = bark() -- calls lambda which returns that string + :lines: 47- .. note:: diff --git a/docs/source/api/property.rst b/docs/source/api/property.rst index e9a574c4..f944f595 100644 --- a/docs/source/api/property.rst +++ b/docs/source/api/property.rst @@ -13,53 +13,6 @@ property These set of functions create a type which allows a setter and getter pair (or a single getter, or a single setter) to be used to create a variable that is either read-write, read-only, or write-only. When used during :doc:`usertype` construction, it will create a variable that uses the setter/getter member function specified. -.. code-block:: cpp - :caption: player.hpp +.. literalinclude:: ../../../examples/property.cpp :linenos: - class Player { - public: - int get_hp() const { - return hp; - } - - void set_hp( int value ) { - hp = value; - } - - int get_max_hp() const { - return hp; - } - - void set_max_hp( int value ) { - maxhp = value; - } - - private: - int hp = 50; - int maxHp = 50; - } - -.. code-block:: cpp - :caption: game.cpp - :linenos: - - sol::state lua; - lua.open_libraries(sol::lib::base); - - lua.set("theplayer", Player()); - - // Yes, you can register after you set a value and it will - // connect up the usertype automatically - lua.new_usertype( "Player", - "hp", sol::property(&Player::get_hp, &Player::set_hp), - "maxHp", sol::property(&Player::get_max_hp, &Player::set_max_hp) - ); - - -.. code-block:: lua - :caption: game-snippet.lua - - -- variable syntax, calls functions - theplayer.hp = 20 - print(theplayer.hp) \ No newline at end of file diff --git a/docs/source/api/protect.rst b/docs/source/api/protect.rst index 112fa5f8..72a8c06f 100644 --- a/docs/source/api/protect.rst +++ b/docs/source/api/protect.rst @@ -10,24 +10,5 @@ protect ``sol::protect( my_func )`` allows you to protect a function call or member variable call when it is being set to Lua. It can be used with usertypes or when just setting a function into Sol. Below is an example that demonstrates that a call that would normally not error without :doc:`Safety features turned on<../safety>` that instead errors and makes the Lua safety-call wrapper ``pcall`` fail: -.. code-block:: cpp - - struct protect_me { - int gen(int x) { - return x; - } - }; - - sol::state lua; - lua.open_libraries(sol::lib::base); - lua.new_usertype("protect_me", - "gen", sol::protect( &protect_me::gen ) - ); - - lua.script(R"__( - pm = protect_me.new() - value = pcall(pm.gen,pm) - )__"); - ); - bool value = lua["value"]; - // value == false +.. literalinclude:: ../../../examples/protect.cpp + :linenos: diff --git a/docs/source/api/protected_function.rst b/docs/source/api/protected_function.rst index dcb68402..4c78515f 100644 --- a/docs/source/api/protected_function.rst +++ b/docs/source/api/protected_function.rst @@ -42,7 +42,7 @@ The following C++ code will call this function from this file and retrieve the r sol::state lua; - lua.open_file( "pfunc_barks.lua" ); + lua.script_file( "pfunc_barks.lua" ); sol::protected_function problematicwoof = lua["woof"]; problematicwoof.error_handler = lua["got_problems"]; @@ -81,7 +81,7 @@ Alternatively, with a bad or good function call, you can use ``sol::optional`` t sol::state lua; - lua.open_file( "pfunc_barks.lua" ); + lua.script_file( "pfunc_barks.lua" ); sol::protected_function problematicwoof = lua["woof"]; problematicwoof.error_handler = lua["got_problems"]; @@ -104,7 +104,7 @@ If you're confident the result succeeded, you can also just put the type you wan sol::state lua; - lua.open_file( "pfunc_barks.lua" ); + lua.script_file( "pfunc_barks.lua" ); // construct with function + error handler // shorter than old syntax @@ -121,7 +121,7 @@ Finally, it is *important* to note you can set a default handler. The function i sol::state lua; - lua.open_file( "pfunc_barks.lua" ); + lua.script_file( "pfunc_barks.lua" ); // sets got_problems as the default // handler for all protected_function errors sol::protected_function::set_default_handler(lua["got_problems"]); diff --git a/docs/source/api/readonly.rst b/docs/source/api/readonly.rst index e01de48f..2dedc11b 100644 --- a/docs/source/api/readonly.rst +++ b/docs/source/api/readonly.rst @@ -14,56 +14,8 @@ The goal of read-only is to protect a variable set on a usertype or a function. If you are looking to make a read-only table, you need to go through a bit of a complicated song and dance by overriding the ``__index`` metamethod. Here's a complete example on the way to do that using ``sol``: -.. code-block:: cpp - :caption: read-only.cpp - - #define SOL_CHECK_ARGUMENTS - #include - - #include - - struct object { - void my_func() { - std::cout << "hello\n"; - } - }; - - int deny(lua_State* L) { - return luaL_error(L, "HAH! Deniiiiied!"); - } - - int main() { - sol::state lua; - lua.open_libraries(sol::lib::base); - - object my_obj; - - sol::table obj_table = lua.create_named_table("object"); - - sol::table obj_metatable = lua.create_table_with(); - obj_metatable.set_function("my_func", &object::my_func, &my_obj); - // Set whatever else you need to - // on the obj_metatable, - // not on the obj_table itself! - - // Properly self-index metatable to block things - obj_metatable[sol::meta_function::new_index] = deny; - obj_metatable[sol::meta_function::index] = obj_metatable; - - // Set it on the actual table - obj_table[sol::metatable_key] = obj_metatable; - - try { - lua.script(R"( - print(object.my_func) - object["my_func"] = 24 - print(object.my_func) - )"); - } - catch (const std::exception& e) { - std::cout << e.what() << std::endl; - } - return 0; - } +.. literalinclude:: ../../../examples/read_only.cpp + :caption: read_only.cpp + :linenos: It is a verbose example, but it explains everything. Because the process is a bit involved and can have unexpected consequences for users that make their own tables, making read-only tables is something that we ask the users to do themselves with the above code, as getting the semantics right for the dozens of use cases would be tremendously difficult. diff --git a/docs/source/api/stack_reference.rst b/docs/source/api/stack_reference.rst index 3e95a0ab..a72f6e85 100644 --- a/docs/source/api/stack_reference.rst +++ b/docs/source/api/stack_reference.rst @@ -17,43 +17,12 @@ This type is particular to working with the stack. It does not push the function Furthermore, if you know you have a function in the right place alongside proper arguments on top of it, you can use the ``sol::stack_count`` structure and give its constructor the number of arguments off the top that you want to call your pre-prepared function with: -.. code-block:: cpp +.. literalinclude:: ../../../examples/stack_aligned_function.cpp :caption: stack_aligned_function.cpp :linenos: - :name: stack-top-example + :name: stack-aligned-function-example - #define SOL_CHECK_ARGUMENTS 1 - #include - - #include - - int main (int, char*[]) { - sol::state lua; - lua.set_function("func", [](int a, int b) { return (a + b) * 2;}); - - sol::reference func_ref = lua["func"]; - lua_State* L = lua.lua_state(); - - // for some reason, you need to use the low-level API - func_ref.push(); // function on stack now - - // maybe this is in a lua_CFunction you bind, - // or maybe you're trying to work with a pre-existing system - sol::stack_aligned_function func(L, -1); - lua_pushinteger(L, 5); // argument 1 - lua_pushinteger(L, 6); // argument 2 - // take 2 arguments from the top, - // and use "stack_aligned_function" to call - int result = func(sol::stack_count(2)); - - // make sure everything is clean - assert(result == 22); - assert(lua.stack_top() == 0); // stack is empty/balanced - - return 0; - } - -Finally, there is a special abstraction that provides further stack optimizations for ``sol::protected_function`` variants that are aligned, and it is called ``sol::stack_aligned_stack_handler_protected_function``. This typedef expects you to pass a ``stack_reference`` handler to its constructor, meaning that you have already placed an appropriate error-handling function somewhere on the stack before the aligned function. You can use ``sol::stack_count`` with this type as well, +Finally, there is a special abstraction that provides further stack optimizations for ``sol::protected_function`` variants that are aligned, and it is called ``sol::stack_aligned_stack_handler_protected_function``. This typedef expects you to pass a ``stack_reference`` handler to its constructor, meaning that you have already placed an appropriate error-handling function somewhere on the stack before the aligned function. You can use ``sol::stack_count`` with this type as well. .. warning:: diff --git a/docs/source/api/table.rst b/docs/source/api/table.rst index 8a217d1f..551b89f4 100644 --- a/docs/source/api/table.rst +++ b/docs/source/api/table.rst @@ -85,6 +85,8 @@ These functions set items into the table. The first one (``set``) can set *mult If the keys within nested queries try to traverse into a table that doesn't exist, it will first pull out a ``nil`` value. If there are further lookups past a key that do not exist, the additional lookups into the nil-returned variable will cause a panic to be fired by the lua C API. +Please note how callables and lambdas are serialized, as there may be issues on GCC-based implementations. See this :ref:`note here`. + This function does not create tables where they do not exist. .. code-block:: cpp @@ -99,6 +101,8 @@ This function does not create tables where they do not exist. Similar to :ref:`set`, but it does so "raw" (ignoring metamethods on the table's metatable). +Please note how callables and lambdas are serialized, as there may be issues on GCC-based implementations. See this :ref:`note here`. + .. note:: Value semantics are applied to all set operations. If you do not ``std::ref( obj )`` or specifically make a pointer with ``std::addressof( obj )`` or ``&obj``, it will copy / move. This is different from how :doc:`sol::function` behaves with its call operator. Also note that this does not detect callables by default: see the :ref:`note here`. @@ -220,6 +224,8 @@ A functional ``for_each`` loop that calls the desired function. The passed in fu Generates a :doc:`proxy` that is templated on the table type and the key type. Enables lookup of items and their implicit conversion to a desired type. Lookup is done lazily. +Please note how callables and lambdas are serialized, as there may be issues on GCC-based implementations. See this :ref:`note here`. + .. code-block:: cpp :caption: function: create a table with defaults :name: table-create @@ -256,4 +262,4 @@ Creates a table, optionally with the specified values pre-set into the table. It Creates a table, optionally with the specified values pre-set into the table, and sets it as the key ``name`` in the table. Applies the same rules as :ref:`table.set` when putting the argument values into the table, including how it handles callable objects. -.. _input iterators: http://en.cppreference.com/w/cpp/concept/InputIterator \ No newline at end of file +.. _input iterators: http://en.cppreference.com/w/cpp/concept/InputIterator diff --git a/docs/source/api/tie.rst b/docs/source/api/tie.rst index ce542127..2074d9e0 100644 --- a/docs/source/api/tie.rst +++ b/docs/source/api/tie.rst @@ -5,22 +5,7 @@ tie `std::tie()`_ does not work well with :doc:`sol::function`'s ``sol::function_result`` returns. Use ``sol::tie`` instead. Because they're both named `tie`, you'll need to be explicit when you use Sol's by naming it with the namespace (``sol::tie``), even with a ``using namespace sol;``. Here's an example: -.. code-block:: cpp - - sol::state lua; - lua.open_libraries(sol::lib::base); - - const auto& code = R"( - function test() - return 1, 2, 3 - end - )"; - lua.script(code); - - int a, b, c; - //std::tie(a, b, c) = lua["test"](); - // will error: use the line below - sol::tie(a, b, c) = lua["test"](); - +.. literalinclude:: ../../../examples/tie.cpp + :linenos: .. _std::tie(): http://en.cppreference.com/w/cpp/utility/tuple/tie diff --git a/docs/source/api/usertype.rst b/docs/source/api/usertype.rst index a5732b8d..9905c733 100644 --- a/docs/source/api/usertype.rst +++ b/docs/source/api/usertype.rst @@ -69,9 +69,9 @@ Equivalently, you can also write: ); // set usertype explicitly, with the given name - lua.set_usertype( "ship", shiptype ); + lua.set_usertype( "ship", shiptype ); - // shiptype is now a useless skeleton type, just let it destruct naturally and don't use it again. + // the shiptype variable is now a useless skeleton type, just let it destruct naturally and don't use it again. Note that here, because the C++ class is default-constructible, it will automatically generate a creation function that can be called in lua called "new" that takes no arguments. You can use it like this in lua code: @@ -285,39 +285,17 @@ usertype arguments - simple usertype - Should probably not be used directly. Use ``sol::table::new_usertype`` or ``sol::table::new_simple_usertype`` instead + + runtime functions ----------------- You can add functions at runtime **to the whole class** (not to individual objects). Set a name under the metatable name you bound using ``new_usertype``/``new_simple_usertype`` to an object. For example: -.. code-block:: cpp - :linenos: +.. literalinclude:: ../../../examples/docs/runtime_extension.cpp :caption: runtime_extension.cpp - :name: runtime-extension - - #define SOL_CHECK_ARGUMENTS 1 - #include - - struct object { - int value = 0; - }; - - int main (int, char*[]) { - - sol::state lua; - lua.open_libraries(sol::lib::base); - - lua.new_usertype( "object" ); - - // runtime additions: through the sol API - lua["object"]["func"] = [](object& o) { return o.value; }; - // runtime additions: through a lua script - lua.script("function object:print () print(self:func()) end"); - - // see it work - lua.script("local obj = object.new() \n obj:print()"); - } - + :name: runtime-extension-example + :linenos: .. note:: @@ -341,35 +319,18 @@ If your class has no complicated™ virtual inheritance or multiple inheritance, For the rest of us safe individuals out there: You must specify the ``sol::base_classes`` tag with the ``sol::bases()`` argument, where ``Types...`` are all the base classes of the single type ``T`` that you are making a usertype out of. +Register the base classes explicitly. + .. note:: Always specify your bases if you plan to retrieve a base class using the Sol abstraction directly and not casting yourself. -.. code-block:: cpp - :linenos: - - struct A { - int a = 10; - virtual int call() { return 0; } - }; - struct B : A { - int b = 11; - virtual int call() override { return 20; } - }; - -Then, to register the base classes explicitly: - -.. code-block:: cpp +.. literalinclude:: ../../../examples/docs/inheritance.cpp + :caption: inheritance.cpp + :name: inheritance-example :linenos: :emphasize-lines: 5 - sol::state lua; - - lua.new_usertype( "B", - "call", &B::call, - sol::base_classes, sol::bases() - ); - .. note:: You must list ALL base classes, including (if there were any) the base classes of A, and the base classes of those base classes, etc. if you want Sol/Lua to handle them automagically. diff --git a/docs/source/containers.rst b/docs/source/containers.rst index a3083240..eae61b1d 100644 --- a/docs/source/containers.rst +++ b/docs/source/containers.rst @@ -12,7 +12,7 @@ Containers are objects that are meant to be inspected and iterated and whose job * This means containers **do not automatically serialize as Lua tables** - If you need tables, consider using ``sol::as_table`` and ``sol::nested`` - See `this table serialization example`_ for more details -* Lua 5.1 has different semantics for ``pairs`` and ``ipairs``: see the example for plain containers in the :doc:`api documentation page for containers` +* Lua 5.1 has different semantics for ``pairs`` and ``ipairs``: be wary. See :ref:`examples down below` for more details * You can override container behavior by overriding :ref:`the detection trait` and :ref:`specializing the container_traits template` * You can bind typical C-style arrays, but must follow :ref:`the rules` @@ -232,7 +232,7 @@ Note that this will not work well in Lua 5.1, as it has explicit table checks an There are also other ways to iterate over key/values, but they can be difficult AND cost your performance due to not having proper support in Lua 5.1. We recommend that you upgrade to Lua 5.2 or 5.3 if this is integral to your infrastructure. -If you can't upgrade, use the member-function ``my_container:pairs()`` in Lua to perform iteration: +If you can't upgrade, use the "member" function ``my_container:pairs()`` in Lua to perform iteration: .. literalinclude:: ../../examples/container_with_pairs.cpp :name: containers-pairs-example diff --git a/docs/source/functions.rst b/docs/source/functions.rst index 69b00cbb..be28337f 100644 --- a/docs/source/functions.rst +++ b/docs/source/functions.rst @@ -29,7 +29,15 @@ There are a number of examples dealing with functions and how they can be bound working with callables/lambdas ------------------------------ -To be explicit about wanting a struct to be interpreted as a function, use ``mytable.set_function( key, func_value );``. You can also use the :doc:`sol::as_function<../api/as_function>` call, which will wrap and identify your type as a function. +To be explicit about wanting a struct to be interpreted as a function, use ``my_table.set_function( key, func_value );``. You can also use the :doc:`sol::as_function<../api/as_function>` call, which will wrap and identify your type as a function. + +.. _lambda-registry: +.. note:: + + When you set lambdas/callables through ``my_table.set( ... )`` using the same function signature, you can suffer from ``const static`` data (like string literals) from not "behaving properly". This is because some compilers do not provide unique type names that we can get at in C++ with RTTI disabled, and thusly it will register the first lambda of the specific signature as the one that will be called. The result is that string literals and other data stored in an compiler implementation-defined manner might be folded and the wrong routine run, even if other observable side effects are nice. + + To avoid this problem, register all your lambdas with :ref:`my_table.set_function` and `avoid the nightmare altogether`_. + Furthermore, it is important to know that lambdas without a specified return type (and a non-const, non-reference-qualified ``auto``) will decay return values. To capture or return references explicitly, use ``decltype(auto)`` or specify the return type **exactly** as desired: @@ -37,7 +45,6 @@ Furthermore, it is important to know that lambdas without a specified return typ :name: refereces-in-lambdas-example :linenos: - .. _function-exception-handling: exception safety/handling @@ -97,3 +104,4 @@ Note that stateless lambdas can be converted to a function pointer, so stateless .. _the examples: https://github.com/ThePhD/sol2/blob/develop/examples/functions.cpp +.. _avoid the nightmare altogether: https://github.com/ThePhD/sol2/issues/608#issuecomment-372876206 diff --git a/docs/source/usertypes.rst b/docs/source/usertypes.rst index 5353ebb3..c7169576 100644 --- a/docs/source/usertypes.rst +++ b/docs/source/usertypes.rst @@ -26,6 +26,14 @@ The examples folder also has a number of really great examples for you to see. T * (Advanced) You can override the iteration function for Lua 5.2 and above (LuaJIT does not have the capability) `as shown in the pairs example`_ * (Advanced) Interop with ``toLua``, ``kaguya``, ``OOLua``, ``LuaBind``, ``luwra``, and all other existing libraries by using the stack API's ``sol::stack::userdata_checker`` and ``sol::stack::userdata_getter`` :ref:`extension points` - Must turn on ``SOL_ENABLE_INTEROP``, as defined in the :ref:`configuration and safety documentation`, to use + +.. _usertype-special-features: +.. note:: + + Note that to use many of sol2's features, such as automatic constructor creation, ``sol::property``, and similar, one must pass these things to the usertype as part of its initial creation and grouping of arguments. Attempting to do so afterwards will result in unexpected and wrong behavior, as the system will be missing information it needs. This is because many of these features rely on ``__index`` and ``__newindex`` Lua metamethods being overridden and handled in a special way! + +Here are some other general advice and tips for understanding and dealing with usertypes: + * Please note that the colon is necessary to "automatically" pass the ``this``/``self`` argument to Lua methods - ``obj:method_name()`` is how you call "member" methods in Lua - It is purely syntactic sugar that passes the object name as the first argument to the ``method_name`` function diff --git a/examples/any_return.cpp b/examples/any_return.cpp index df82f9c5..10a44abf 100644 --- a/examples/any_return.cpp +++ b/examples/any_return.cpp @@ -39,7 +39,7 @@ int main() { // result3 == 16.5 c_assert(result3 == 16.5); - std::cout << "=== any_return example ===" << std::endl; + std::cout << "=== any_return ===" << std::endl; std::cout << "result : " << result << std::endl; std::cout << "result2: " << result2 << std::endl; std::cout << "result3: " << result3 << std::endl; diff --git a/examples/args_from_container.cpp b/examples/args_from_container.cpp new file mode 100644 index 00000000..5e9eec4e --- /dev/null +++ b/examples/args_from_container.cpp @@ -0,0 +1,31 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include +#include +#include + +int main(int , const char*[]) { + + std::cout << "=== args_from_container ===" << std::endl; + + sol::state lua; + lua.open_libraries(); + + lua.script("function f (a, b, c, d) print(a, b, c, d) end"); + + sol::function f = lua["f"]; + + std::vector v2{ 3, 4 }; + f(1, 2, sol::as_args(v2)); + + std::set v4{ 3, 1, 2, 4 }; + f(sol::as_args(v4)); + + int v3[] = { 2, 3, 4 }; + f(1, sol::as_args(v3)); + + std::cout << std::endl; + + return 0; +} \ No newline at end of file diff --git a/examples/as_returns.cpp b/examples/as_returns.cpp new file mode 100644 index 00000000..bf56d59b --- /dev/null +++ b/examples/as_returns.cpp @@ -0,0 +1,28 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include +#include + +int main () { + sol::state lua; + + lua.set_function("f", []() { + std::set results{ "arf", "bark", "woof" }; + return sol::as_returns(std::move(results)); + }); + + lua.script("v1, v2, v3 = f()"); + + std::string v1 = lua["v1"]; + std::string v2 = lua["v2"]; + std::string v3 = lua["v3"]; + + c_assert(v1 == "arf"); + c_assert(v2 == "bark"); + c_assert(v3 == "woof"); + + return 0; +} diff --git a/examples/basic.cpp b/examples/basic.cpp index 04b8a95a..d2a919be 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -5,7 +5,7 @@ #include "assert.hpp" int main() { - std::cout << "=== basic example ===" << std::endl; + std::cout << "=== basic ===" << std::endl; // create an empty lua state sol::state lua; diff --git a/examples/c_call.cpp b/examples/c_call.cpp new file mode 100644 index 00000000..ec9799a9 --- /dev/null +++ b/examples/c_call.cpp @@ -0,0 +1,50 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +int f1(int) { return 32; } + +int f2(int, int) { return 1; } + +struct fer { + double f3(int, int) { + return 2.5; + } +}; + + +int main() { + + sol::state lua; + // overloaded function f + lua.set("f", sol::c_call, sol::wrap, sol::wrap>); + // singly-wrapped function + lua.set("g", sol::c_call>); + // without the 'sol::wrap' boilerplate + lua.set("h", sol::c_call); + // object used for the 'fer' member function call + lua.set("obj", fer()); + + // call them like any other bound function + lua.script("r1 = f(1)"); + lua.script("r2 = f(1, 2)"); + lua.script("r3 = f(obj, 1, 2)"); + lua.script("r4 = g(1)"); + lua.script("r5 = h(1, 2)"); + + // get the results and see + // if it worked out + int r1 = lua["r1"]; + c_assert(r1 == 32); + int r2 = lua["r2"]; + c_assert(r2 == 1); + double r3 = lua["r3"]; + c_assert(r3 == 2.5); + int r4 = lua["r4"]; + c_assert(r4 == 32); + int r5 = lua["r5"]; + c_assert(r5 == 1); + + return 0; +} diff --git a/examples/calling_lua_functions.cpp b/examples/calling_lua_functions.cpp index 5b5fe3cf..d24842a2 100644 --- a/examples/calling_lua_functions.cpp +++ b/examples/calling_lua_functions.cpp @@ -23,7 +23,7 @@ sol::variadic_results fallback(sol::this_state ts, sol::variadic_args args) { } int main(int, char*[]) { - std::cout << "=== calling lua functions example ===" << std::endl; + std::cout << "=== calling lua functions ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/config.cpp b/examples/config.cpp index e44bb079..f4098a12 100644 --- a/examples/config.cpp +++ b/examples/config.cpp @@ -36,7 +36,7 @@ height = 1080 c_assert(screen.width == 1920); c_assert(screen.height == 1080); - std::cout << "=== config example ===" << std::endl; + std::cout << "=== config ===" << std::endl; screen.print(); std::cout << std::endl; } diff --git a/examples/container_usertype_as_container.cpp b/examples/container_usertype_as_container.cpp index 8e43c342..7156f2de 100644 --- a/examples/container_usertype_as_container.cpp +++ b/examples/container_usertype_as_container.cpp @@ -36,7 +36,7 @@ namespace sol { } int main(int, char*[]) { - std::cout << "=== container as container example ===" << std::endl; + std::cout << "=== container as container ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/container_with_pairs.cpp b/examples/container_with_pairs.cpp index 3ec808c6..f6455076 100644 --- a/examples/container_with_pairs.cpp +++ b/examples/container_with_pairs.cpp @@ -18,7 +18,7 @@ int main() { using my_set = std::unordered_set, hasher>; - std::cout << "=== containers with std::pair<> example ===" << std::endl; + std::cout << "=== containers with std::pair<> ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/containers.cpp b/examples/containers.cpp index 4314dea8..8153353f 100644 --- a/examples/containers.cpp +++ b/examples/containers.cpp @@ -5,7 +5,7 @@ #include int main(int, char**) { - std::cout << "=== containers example ===" << std::endl; + std::cout << "=== containers ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/containers_as_table.cpp b/examples/containers_as_table.cpp index 663b6c15..d1e86dcd 100644 --- a/examples/containers_as_table.cpp +++ b/examples/containers_as_table.cpp @@ -54,7 +54,7 @@ void demo_explicit (sol::as_table_t int main() { - std::cout << "=== coroutine example ===" << std::endl; + std::cout << "=== coroutine ===" << std::endl; sol::state lua; std::vector tasks; diff --git a/examples/coroutine_state.cpp b/examples/coroutine_state.cpp index e1f3264b..5b8edff2 100644 --- a/examples/coroutine_state.cpp +++ b/examples/coroutine_state.cpp @@ -5,7 +5,7 @@ #include int main(int, char*[]) { - std::cout << "=== coroutine state transfer example ===" << std::endl; + std::cout << "=== coroutine state transfer ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/customization.cpp b/examples/customization.cpp index f56ff9cb..46e15e43 100644 --- a/examples/customization.cpp +++ b/examples/customization.cpp @@ -72,7 +72,7 @@ namespace sol { } int main() { - std::cout << "=== customization example ===" << std::endl; + std::cout << "=== customization ===" << std::endl; std::cout << std::boolalpha; sol::state lua; diff --git a/examples/docs/as_function.cpp b/examples/docs/as_function.cpp new file mode 100644 index 00000000..8d0a1f33 --- /dev/null +++ b/examples/docs/as_function.cpp @@ -0,0 +1,21 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +int main () { + struct callable { + int operator()( int a, bool b ) { + return a + b ? 10 : 20; + } + }; + + + sol::state lua; + // Binds struct as userdata + // can still be callable, but beware + // caveats + lua.set( "not_func", callable() ); + // Binds struct as function + lua.set( "func", sol::as_function( callable() ) ); + // equivalent: lua.set_function( "func", callable() ); + // equivalent: lua["func"] = callable(); +} diff --git a/examples/docs/as_function_usertype_member_variable.cpp b/examples/docs/as_function_usertype_member_variable.cpp new file mode 100644 index 00000000..5cc5e0f6 --- /dev/null +++ b/examples/docs/as_function_usertype_member_variable.cpp @@ -0,0 +1,27 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +int main () { + class B { + public: + int bvar = 24; + }; + + sol::state lua; + lua.open_libraries(); + lua.new_usertype("B", + // bind as variable + "b", &B::bvar, + // bind as function + "f", sol::as_function(&B::bvar) + ); + + B b; + lua.set("b", &b); + lua.script("x = b:f()"); + lua.script("y = b.b"); + int x = lua["x"]; + int y = lua["y"]; + assert(x == 24); + assert(y == 24); +} diff --git a/examples/docs/as_table_ipairs.cpp b/examples/docs/as_table_ipairs.cpp new file mode 100644 index 00000000..df33a8c6 --- /dev/null +++ b/examples/docs/as_table_ipairs.cpp @@ -0,0 +1,14 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +int main (int, char*[]) { + + sol::state lua; + lua.open_libraries(); + lua.set("my_table", sol::as_table(std::vector{ 1, 2, 3, 4, 5 })); + lua.script("for k, v in ipairs(my_table) do print(k, v) assert(k == v) end"); + + return 0; +} diff --git a/examples/docs/inheritance.cpp b/examples/docs/inheritance.cpp new file mode 100644 index 00000000..942f1846 --- /dev/null +++ b/examples/docs/inheritance.cpp @@ -0,0 +1,27 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +struct A { + int a = 10; + virtual int call() { return 0; } +}; +struct B : A { + int b = 11; + virtual int call() override { return 20; } +}; + +int main (int, char*[]) { + + sol::state lua; + + lua.new_usertype( "A", + "call", &A::call + ); + + lua.new_usertype( "B", + "call", &B::call, + sol::base_classes, sol::bases() + ); + + return 0; +} diff --git a/examples/docs/preparing_environments.cpp b/examples/docs/preparing_environments.cpp new file mode 100644 index 00000000..d220abed --- /dev/null +++ b/examples/docs/preparing_environments.cpp @@ -0,0 +1,29 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +int main (int, char*[]) { + sol::state lua; + lua.open_libraries(); + sol::environment my_env(lua, sol::create); + // set value, and we need to explicitly allow for + // access to "print", since a new environment hides + // everything that's not defined inside of it + // NOTE: hiding also hides library functions (!!) + // BE WARNED + my_env["var"] = 50; + my_env["print"] = lua["print"]; + + sol::environment my_other_env(lua, sol::create, lua.globals()); + // do not need to explicitly allow access to "print", + // since we used the "Set a fallback" version + // of the sol::environment constructor + my_other_env["var"] = 443; + + // output: 50 + lua.script("print(var)", my_env); + + // output: 443 + lua.script("print(var)", my_other_env); + + return 0; +} \ No newline at end of file diff --git a/examples/docs/runtime_extension.cpp b/examples/docs/runtime_extension.cpp new file mode 100644 index 00000000..fc690238 --- /dev/null +++ b/examples/docs/runtime_extension.cpp @@ -0,0 +1,28 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +struct object { + int value = 0; +}; + +int main (int, char*[]) { + + std::cout << "==== runtime_extension =====" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype( "object" ); + + // runtime additions: through the sol API + lua["object"]["func"] = [](object& o) { return o.value; }; + // runtime additions: through a lua script + lua.script("function object:print () print(self:func()) end"); + + // see it work + lua.script("local obj = object.new() \n obj:print()"); + + return 0; +} diff --git a/examples/dynamic_object.cpp b/examples/dynamic_object.cpp index f4f13b5c..d5224f05 100644 --- a/examples/dynamic_object.cpp +++ b/examples/dynamic_object.cpp @@ -33,7 +33,7 @@ struct dynamic_object { int main() { - std::cout << "=== dynamic_object example ===" << std::endl; + std::cout << "=== dynamic_object ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/environment_snooping.cpp b/examples/environment_snooping.cpp index cd980c0f..1c5fb5ae 100644 --- a/examples/environment_snooping.cpp +++ b/examples/environment_snooping.cpp @@ -70,7 +70,7 @@ void complicated(sol::this_state ts) { } int main() { - std::cout << "=== environment snooping example ===" << std::endl; + std::cout << "=== environment snooping ===" << std::endl; sol::state lua; sol::environment freshenv(lua, sol::create, lua.globals()); diff --git a/examples/environment_state.cpp b/examples/environment_state.cpp index 1949f660..0dcc8d8a 100644 --- a/examples/environment_state.cpp +++ b/examples/environment_state.cpp @@ -4,7 +4,7 @@ #include int main(int, char*[]) { - std::cout << "=== environment state example ===" << std::endl; + std::cout << "=== environment state ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/environment_state_2.cpp b/examples/environment_state_2.cpp index d9f29462..8ceb9a51 100644 --- a/examples/environment_state_2.cpp +++ b/examples/environment_state_2.cpp @@ -4,7 +4,7 @@ #include int main(int, char* []) { - std::cout << "=== environment state 2 example ===" << std::endl; + std::cout << "=== environment state 2 ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/environments.cpp b/examples/environments.cpp index ef643117..254a41a8 100644 --- a/examples/environments.cpp +++ b/examples/environments.cpp @@ -21,7 +21,7 @@ void test_environment(std::string key, const sol::environment& env, const sol::s } int main(int, char**) { - std::cout << "=== environments example ===" << std::endl; + std::cout << "=== environments ===" << std::endl; sol::state lua; // A global variable to see if we can "fallback" into it diff --git a/examples/exception_handler.cpp b/examples/exception_handler.cpp index d84d28f7..31b48a05 100644 --- a/examples/exception_handler.cpp +++ b/examples/exception_handler.cpp @@ -33,7 +33,7 @@ void will_throw() { } int main() { - std::cout << "=== exception_handler example ===" << std::endl; + std::cout << "=== exception_handler ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/functions.cpp b/examples/functions.cpp index c3c5da44..4289402f 100644 --- a/examples/functions.cpp +++ b/examples/functions.cpp @@ -19,7 +19,7 @@ struct multiplier { }; int main() { - std::cout << "=== functions example ===" << std::endl; + std::cout << "=== functions ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/index_and_newindex_usertype.cpp b/examples/index_and_newindex_usertype.cpp new file mode 100644 index 00000000..dae54b2d --- /dev/null +++ b/examples/index_and_newindex_usertype.cpp @@ -0,0 +1,44 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +class vector { +public: + double data[3]; + + vector() : data{ 0,0,0 } {} + + double& operator[](int i) { + return data[i]; + } + + + static double my_index(vector& v, int i) { + return v[i]; + } + + static void my_new_index(vector& v, int i, double x) { + v[i] = x; + } +}; + +int main () { + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("vector", sol::constructors>(), + sol::meta_function::index, &vector::my_index, + sol::meta_function::new_index, &vector::my_new_index); + lua.script("v = vector.new()\n" + "print(v[1])\n" + "v[2] = 3\n" + "print(v[2])\n" + ); + + vector& v = lua["v"]; + c_assert(v[0] == 0.0); + c_assert(v[1] == 0.0); + c_assert(v[2] == 3.0); + + return 0; +} diff --git a/examples/indirect_function_calls.cpp b/examples/indirect_function_calls.cpp index ebaa209d..c9e5290c 100644 --- a/examples/indirect_function_calls.cpp +++ b/examples/indirect_function_calls.cpp @@ -34,7 +34,7 @@ sol::variadic_results call_it(sol::object function_name, sol::variadic_args args } int main() { - std::cout << "=== indirect function calls example ===" << std::endl; + std::cout << "=== indirect function calls ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/metatable_key_low_level.cpp b/examples/metatable_key_low_level.cpp new file mode 100644 index 00000000..9c3790d4 --- /dev/null +++ b/examples/metatable_key_low_level.cpp @@ -0,0 +1,35 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +int main () { + + struct bark { + int operator()(int x) { + return x; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("bark", + sol::meta_function::call_function, &bark::operator() + ); + + bark b; + lua.set("b", &b); + + sol::table b_as_table = lua["b"]; + sol::table b_metatable = b_as_table[sol::metatable_key]; + sol::function b_call = b_metatable["__call"]; + sol::function b_as_function = lua["b"]; + + int result1 = b_as_function(1); + // pass 'self' directly to argument + int result2 = b_call(b, 1); + c_assert(result1 == result2); + c_assert(result1 == 1); + c_assert(result2 == 1); +} diff --git a/examples/multi_results.cpp b/examples/multi_results.cpp index 9ff90453..217af3e7 100644 --- a/examples/multi_results.cpp +++ b/examples/multi_results.cpp @@ -6,7 +6,7 @@ #include int main() { - std::cout << "=== multi results example ===" << std::endl; + std::cout << "=== multi results ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/optional_with_iteration.cpp b/examples/optional_with_iteration.cpp index 2ead0d1f..17b4f4da 100644 --- a/examples/optional_with_iteration.cpp +++ b/examples/optional_with_iteration.cpp @@ -6,7 +6,7 @@ #include int main(int, char**) { - std::cout << "=== optional with iteration example ===" << std::endl; + std::cout << "=== optional with iteration ===" << std::endl; struct thing { int a = 20; diff --git a/examples/overloading.cpp b/examples/overloading.cpp index 7dc71320..32afddeb 100644 --- a/examples/overloading.cpp +++ b/examples/overloading.cpp @@ -13,7 +13,7 @@ inline std::string make_string(std::string input) { } int main() { - std::cout << "=== overloading example ===" << std::endl; + std::cout << "=== overloading ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/overloading_with_fallback.cpp b/examples/overloading_with_fallback.cpp index 4a59b7e1..44b1ddef 100644 --- a/examples/overloading_with_fallback.cpp +++ b/examples/overloading_with_fallback.cpp @@ -23,7 +23,7 @@ sol::variadic_results fallback(sol::this_state ts, sol::variadic_args args) { } int main(int, char*[]) { - std::cout << "=== overloading with fallback example ===" << std::endl; + std::cout << "=== overloading with fallback ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/overloading_with_members.cpp b/examples/overloading_with_members.cpp new file mode 100644 index 00000000..94e338eb --- /dev/null +++ b/examples/overloading_with_members.cpp @@ -0,0 +1,66 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include + +struct pup { + int barks = 0; + + void bark () { + ++barks; // bark! + } + + bool is_cute () const { + return true; + } +}; + +void ultra_bark( pup& p, int barks) { + for (; barks --> 0;) p.bark(); +} + +void picky_bark( pup& p, std::string s) { + if ( s == "bark" ) + p.bark(); +} + +int main () { + std::cout << "=== overloading with members ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function( "bark", sol::overload( + ultra_bark, + []() { return "the bark from nowhere"; } + ) ); + + lua.new_usertype( "pup", + // regular function + "is_cute", &pup::is_cute, + // overloaded function + "bark", sol::overload( &pup::bark, &picky_bark ) + ); + + const auto& code = R"( + barker = pup.new() + print(barker:is_cute()) + barker:bark() -- calls member function pup::bark + barker:bark("meow") -- picky_bark, no bark + barker:bark("bark") -- picky_bark, bark + + bark(barker, 20) -- calls ultra_bark + print(bark()) -- calls lambda which returns that string + )"; + + lua.script(code); + + pup& barker = lua["barker"]; + std::cout << barker.barks << std::endl; + c_assert(barker.barks == 22); + + std::cout << std::endl; + return 0; +} \ No newline at end of file diff --git a/examples/property.cpp b/examples/property.cpp new file mode 100644 index 00000000..b4f721f2 --- /dev/null +++ b/examples/property.cpp @@ -0,0 +1,57 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include + +class Player { +public: + int get_hp() const { + return hp; + } + + void set_hp( int value ) { + hp = value; + } + + int get_max_hp() const { + return hp; + } + + void set_max_hp( int value ) { + maxhp = value; + } + +private: + int hp = 50; + int maxhp = 50; +}; + +int main (int, char*[]) { + + std::cout << "=== properties from C++ functions ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set("theplayer", Player()); + + // Yes, you can register after you set a value and it will + // connect up the usertype automatically + lua.new_usertype( "Player", + "hp", sol::property(&Player::get_hp, &Player::set_hp), + "maxHp", sol::property(&Player::get_max_hp, &Player::set_max_hp) + ); + + const auto& code = R"( + -- variable syntax, calls functions + theplayer.hp = 20 + print('hp:', theplayer.hp) + print('max hp:', theplayer.maxHp) + )"; + + lua.script(code); + + return 0; +} diff --git a/examples/protect.cpp b/examples/protect.cpp new file mode 100644 index 00000000..95de7232 --- /dev/null +++ b/examples/protect.cpp @@ -0,0 +1,27 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +int main () { + struct protect_me { + int gen(int x) { + return x; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + lua.new_usertype("protect_me", + "gen", sol::protect( &protect_me::gen ) + ); + + lua.script(R"__( + pm = protect_me.new() + value = pcall(pm.gen,"wrong argument") + )__"); + bool value = lua["value"]; + c_assert(!value); + + return 0; +} diff --git a/examples/protected_functions.cpp b/examples/protected_functions.cpp index 7f842f0f..ce8ba289 100644 --- a/examples/protected_functions.cpp +++ b/examples/protected_functions.cpp @@ -4,7 +4,7 @@ #include int main() { - std::cout << "=== protected_functions example ===" << std::endl; + std::cout << "=== protected_functions ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/read_only.cpp b/examples/read_only.cpp new file mode 100644 index 00000000..1e01ff87 --- /dev/null +++ b/examples/read_only.cpp @@ -0,0 +1,48 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +struct object { + void my_func() { + std::cout << "hello\n"; + } +}; + +int deny(lua_State* L) { + return luaL_error(L, "HAH! Deniiiiied!"); +} + +int main() { + sol::state lua; + lua.open_libraries(sol::lib::base); + + object my_obj; + + sol::table obj_table = lua.create_named_table("object"); + + sol::table obj_metatable = lua.create_table_with(); + obj_metatable.set_function("my_func", &object::my_func, &my_obj); + // Set whatever else you need to + // on the obj_metatable, + // not on the obj_table itself! + + // Properly self-index metatable to block things + obj_metatable[sol::meta_function::new_index] = deny; + obj_metatable[sol::meta_function::index] = obj_metatable; + + // Set it on the actual table + obj_table[sol::metatable_key] = obj_metatable; + + try { + lua.script(R"( +print(object.my_func) +object["my_func"] = 24 +print(object.my_func) + )"); + } + catch (const std::exception& e) { + std::cout << "an expected error occurred: " << e.what() << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/examples/require.cpp b/examples/require.cpp index 96c164eb..4398c396 100644 --- a/examples/require.cpp +++ b/examples/require.cpp @@ -25,7 +25,7 @@ sol::table open_mylib(sol::this_state s) { } int main() { - std::cout << "=== require example ===" << std::endl; + std::cout << "=== require ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::package, sol::lib::base); diff --git a/examples/require_dll_example/require_from_dll.cpp b/examples/require_dll_example/require_from_dll.cpp index 1fad2cde..76ee167a 100644 --- a/examples/require_dll_example/require_from_dll.cpp +++ b/examples/require_dll_example/require_from_dll.cpp @@ -7,7 +7,7 @@ #include int main(int, char*[]) { - std::cout << "=== require from DLL example ===" << std::endl; + std::cout << "=== require from DLL ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::package, sol::lib::base); diff --git a/examples/runtime_additions.cpp b/examples/runtime_additions.cpp index b10f8ef8..bad891ca 100644 --- a/examples/runtime_additions.cpp +++ b/examples/runtime_additions.cpp @@ -9,7 +9,7 @@ struct object { }; int main(int, char*[]) { - std::cout << "=== runtime_additions example ===" << std::endl; + std::cout << "=== runtime_additions ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/script_error_handling.cpp b/examples/script_error_handling.cpp index 2968c363..bbf5aaa8 100644 --- a/examples/script_error_handling.cpp +++ b/examples/script_error_handling.cpp @@ -5,7 +5,7 @@ #include int main(int, char**) { - std::cout << "=== script error handling example ===" << std::endl; + std::cout << "=== script error handling ===" << std::endl; sol::state lua; diff --git a/examples/singleton.cpp b/examples/singleton.cpp index 9c3ca1f1..fcd59cd5 100644 --- a/examples/singleton.cpp +++ b/examples/singleton.cpp @@ -40,7 +40,7 @@ std::shared_ptr SomeLib::getInstance() { } int main(int, char*[]) { - std::cout << "=== singleton example ===" << std::endl; + std::cout << "=== singleton ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/static_variables.cpp b/examples/static_variables.cpp index 93e88528..4284f1f0 100644 --- a/examples/static_variables.cpp +++ b/examples/static_variables.cpp @@ -11,7 +11,7 @@ int test::muh_variable = 25; int main() { - std::cout << "=== static_variables example ===" << std::endl; + std::cout << "=== static_variables ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/tables.cpp b/examples/tables.cpp index 1247c796..6e551b98 100644 --- a/examples/tables.cpp +++ b/examples/tables.cpp @@ -7,7 +7,7 @@ // this example shows how to read data in from a lua table int main() { - std::cout << "=== tables example ===" << std::endl; + std::cout << "=== tables ===" << std::endl; sol::state lua; // table used as an array diff --git a/examples/tie.cpp b/examples/tie.cpp new file mode 100644 index 00000000..aadbacb9 --- /dev/null +++ b/examples/tie.cpp @@ -0,0 +1,39 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +int main (int, char*[]) { + + const auto& code = R"( + bark_power = 11; + + function woof ( bark_energy ) + return (bark_energy * (bark_power / 4)) + end +)"; + + sol::state lua; + + lua.script(code); + + sol::function woof = lua["woof"]; + double numwoof = woof(20); + c_assert(numwoof == 55.0); + + lua.script( "function f () return 10, 11, 12 end" ); + + sol::function f = lua["f"]; + std::tuple abc = f(); + c_assert(std::get<0>(abc) == 10); + c_assert(std::get<1>(abc) == 11); + c_assert(std::get<2>(abc) == 12); + // or + int a, b, c; + sol::tie(a, b, c) = f(); + c_assert(a == 10); + c_assert(b == 11); + c_assert(c == 12); + + return 0; +} diff --git a/examples/tutorials/quick_n_dirty/functions_all.cpp b/examples/tutorials/quick_n_dirty/functions_all.cpp index 2637d661..4a6c5edd 100644 --- a/examples/tutorials/quick_n_dirty/functions_all.cpp +++ b/examples/tutorials/quick_n_dirty/functions_all.cpp @@ -21,7 +21,7 @@ struct some_class { }; int main(int, char*[]) { - std::cout << "=== functions (all) example ===" << std::endl; + std::cout << "=== functions (all) ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/tutorials/quick_n_dirty/namespacing.cpp b/examples/tutorials/quick_n_dirty/namespacing.cpp index 13658d7d..e00cee8c 100644 --- a/examples/tutorials/quick_n_dirty/namespacing.cpp +++ b/examples/tutorials/quick_n_dirty/namespacing.cpp @@ -5,7 +5,7 @@ #include "../../assert.hpp" int main() { - std::cout << "=== namespacing example ===" << std::endl; + std::cout << "=== namespacing ===" << std::endl; struct my_class { int b = 24; diff --git a/examples/tutorials/quick_n_dirty/opening_a_state.cpp b/examples/tutorials/quick_n_dirty/opening_a_state.cpp index 3c6d309f..7f1c20d0 100644 --- a/examples/tutorials/quick_n_dirty/opening_a_state.cpp +++ b/examples/tutorials/quick_n_dirty/opening_a_state.cpp @@ -5,7 +5,7 @@ #include "../../assert.hpp" int main(int, char*[]) { - std::cout << "=== opening a state example ===" << std::endl; + std::cout << "=== opening a state ===" << std::endl; sol::state lua; // open some common libraries diff --git a/examples/tutorials/quick_n_dirty/opening_state_on_raw_lua.cpp b/examples/tutorials/quick_n_dirty/opening_state_on_raw_lua.cpp index d6d1f451..bb974560 100644 --- a/examples/tutorials/quick_n_dirty/opening_state_on_raw_lua.cpp +++ b/examples/tutorials/quick_n_dirty/opening_state_on_raw_lua.cpp @@ -10,7 +10,7 @@ int use_sol2(lua_State* L) { } int main(int, char*[]) { - std::cout << "=== opening sol::state_view on raw Lua example ===" << std::endl; + std::cout << "=== opening sol::state_view on raw Lua ===" << std::endl; lua_State* L = luaL_newstate(); luaL_openlibs(L); diff --git a/examples/tutorials/quick_n_dirty/running_lua_code.cpp b/examples/tutorials/quick_n_dirty/running_lua_code.cpp index dcda4a7e..7e287474 100644 --- a/examples/tutorials/quick_n_dirty/running_lua_code.cpp +++ b/examples/tutorials/quick_n_dirty/running_lua_code.cpp @@ -6,7 +6,7 @@ #include "../../assert.hpp" int main(int, char*[]) { - std::cout << "=== running lua code example ===" << std::endl; + std::cout << "=== running lua code ===" << std::endl; { std::ofstream out("a_lua_script.lua"); diff --git a/examples/tutorials/quick_n_dirty/running_lua_code_low_level.cpp b/examples/tutorials/quick_n_dirty/running_lua_code_low_level.cpp index 04856cfe..4aa1bb30 100644 --- a/examples/tutorials/quick_n_dirty/running_lua_code_low_level.cpp +++ b/examples/tutorials/quick_n_dirty/running_lua_code_low_level.cpp @@ -7,7 +7,7 @@ #include "../../assert.hpp" int main(int, char*[]) { - std::cout << "=== running lua code (low level) example ===" << std::endl; + std::cout << "=== running lua code (low level) ===" << std::endl; { std::ofstream out("a_lua_script.lua"); diff --git a/examples/tutorials/quick_n_dirty/self_call.cpp b/examples/tutorials/quick_n_dirty/self_call.cpp index ef4212a3..10015ba0 100644 --- a/examples/tutorials/quick_n_dirty/self_call.cpp +++ b/examples/tutorials/quick_n_dirty/self_call.cpp @@ -4,7 +4,7 @@ #include int main() { - std::cout << "=== self_call example ===" << std::endl; + std::cout << "=== self_call ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table); diff --git a/examples/tutorials/quick_n_dirty/userdata.cpp b/examples/tutorials/quick_n_dirty/userdata.cpp index c51cba03..e587a5e9 100644 --- a/examples/tutorials/quick_n_dirty/userdata.cpp +++ b/examples/tutorials/quick_n_dirty/userdata.cpp @@ -20,7 +20,7 @@ struct Doge { }; int main(int, char* []) { - std::cout << "=== userdata example ===" << std::endl; + std::cout << "=== userdata ===" << std::endl; sol::state lua; diff --git a/examples/tutorials/quick_n_dirty/userdata_memory_reference.cpp b/examples/tutorials/quick_n_dirty/userdata_memory_reference.cpp index bc4be8c5..e9833ef9 100644 --- a/examples/tutorials/quick_n_dirty/userdata_memory_reference.cpp +++ b/examples/tutorials/quick_n_dirty/userdata_memory_reference.cpp @@ -20,7 +20,7 @@ struct Doge { }; int main(int, char* []) { - std::cout << "=== userdata memory reference example ===" << std::endl; + std::cout << "=== userdata memory reference ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/tutorials/quick_n_dirty/usertypes.cpp b/examples/tutorials/quick_n_dirty/usertypes.cpp index e14e025c..90e77291 100644 --- a/examples/tutorials/quick_n_dirty/usertypes.cpp +++ b/examples/tutorials/quick_n_dirty/usertypes.cpp @@ -19,7 +19,7 @@ struct Doge { }; int main(int, char* []) { - std::cout << "=== usertypes example ===" << std::endl; + std::cout << "=== usertypes ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/usertype.cpp b/examples/usertype.cpp index 0117d98c..32cfa9d5 100644 --- a/examples/usertype.cpp +++ b/examples/usertype.cpp @@ -41,7 +41,7 @@ struct variables { }; int main() { - std::cout << "=== usertype example ===" << std::endl; + std::cout << "=== usertype ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::math); diff --git a/examples/usertype_advanced.cpp b/examples/usertype_advanced.cpp index 9866edea..377b549a 100644 --- a/examples/usertype_advanced.cpp +++ b/examples/usertype_advanced.cpp @@ -47,7 +47,7 @@ private: }; int main() { - std::cout << "=== usertype_advanced example ===" << std::endl; + std::cout << "=== usertype_advanced ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/usertype_automatic_operators.cpp b/examples/usertype_automatic_operators.cpp index 2dc34042..1867819b 100644 --- a/examples/usertype_automatic_operators.cpp +++ b/examples/usertype_automatic_operators.cpp @@ -63,7 +63,7 @@ std::ostream& operator<<(std::ostream& os, const automatic& right) { } int main(int, char*[]) { - std::cout << "=== usertype automatic operators example ===" << std::endl; + std::cout << "=== usertype automatic operators ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/usertype_bitfields.cpp b/examples/usertype_bitfields.cpp index 7c80310e..59171c08 100644 --- a/examples/usertype_bitfields.cpp +++ b/examples/usertype_bitfields.cpp @@ -115,7 +115,7 @@ struct __attribute__((packed, aligned(1))) flags_t { } flags{0, 0, 0, 0, 0, 0, 0, 0, 0}; int main() { - std::cout << "=== usertype_bitfields example ===" << std::endl; + std::cout << "=== usertype_bitfields ===" << std::endl; #ifdef __MINGW32__ std::cout << "MinGW Detected, packing structs is broken in MinGW and this test may fail" << std::endl; #endif diff --git a/examples/usertype_call_from_c++.cpp b/examples/usertype_call_from_c++.cpp index 09db6fcc..befe9b99 100644 --- a/examples/usertype_call_from_c++.cpp +++ b/examples/usertype_call_from_c++.cpp @@ -4,7 +4,7 @@ #include int main(int, char*[]) { - std::cout << "=== usertype call from C++ example ===" << std::endl; + std::cout << "=== usertype call from C++ ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/usertype_dynamic_getter_setter.cpp b/examples/usertype_dynamic_getter_setter.cpp index 67a82aee..1ee41f69 100644 --- a/examples/usertype_dynamic_getter_setter.cpp +++ b/examples/usertype_dynamic_getter_setter.cpp @@ -97,7 +97,7 @@ struct vec { }; int main() { - std::cout << "=== usertype dynamic getter/setter example ===" << std::endl; + std::cout << "=== usertype dynamic getter/setter ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/usertype_initializers.cpp b/examples/usertype_initializers.cpp index 1e4908a0..73157c4c 100644 --- a/examples/usertype_initializers.cpp +++ b/examples/usertype_initializers.cpp @@ -41,7 +41,7 @@ public: }; int main() { - std::cout << "=== usertype_initializers example ===" << std::endl; + std::cout << "=== usertype_initializers ===" << std::endl; { // additional scope to make usertype destroy earlier sol::state lua; lua.open_libraries(); diff --git a/examples/usertype_simple.cpp b/examples/usertype_simple.cpp index 7986514b..dd64a934 100644 --- a/examples/usertype_simple.cpp +++ b/examples/usertype_simple.cpp @@ -25,7 +25,7 @@ struct my_data { }; int main() { - std::cout << "=== usertype_simple example ===" << std::endl; + std::cout << "=== usertype_simple ===" << std::endl; sol::state lua; lua.open_libraries(); diff --git a/examples/variables.cpp b/examples/variables.cpp index c8f5b7fd..a8fea78f 100644 --- a/examples/variables.cpp +++ b/examples/variables.cpp @@ -4,7 +4,7 @@ #include int main() { - std::cout << "=== variables example ===" << std::endl; + std::cout << "=== variables ===" << std::endl; sol::state lua; diff --git a/examples/variadic_args.cpp b/examples/variadic_args.cpp index 591558a2..2305849b 100644 --- a/examples/variadic_args.cpp +++ b/examples/variadic_args.cpp @@ -4,7 +4,7 @@ #include int main() { - std::cout << "=== variadic_args example ===" << std::endl; + std::cout << "=== variadic_args ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/examples/wip/lua_inheritance.cpp b/examples/wip/lua_inheritance.cpp index f07e10ab..aaab52be 100644 --- a/examples/wip/lua_inheritance.cpp +++ b/examples/wip/lua_inheritance.cpp @@ -5,7 +5,7 @@ #include int main(int, char*[]) { - std::cout << "=== lua inheritance example ===" << std::endl; + std::cout << "=== lua inheritance ===" << std::endl; sol::state lua; lua.open_libraries(sol::lib::base); diff --git a/sol/call.hpp b/sol/call.hpp index 90e1f492..cca872ba 100644 --- a/sol/call.hpp +++ b/sol/call.hpp @@ -221,13 +221,9 @@ namespace sol { construct_match(constructor_match(obj), L, argcount, 1 + static_cast(syntax)); userdataref.push(); - luaL_getmetatable(L, &meta[0]); - if (type_of(L, -1) == type::lua_nil) { - lua_pop(L, 1); - return luaL_error(L, "sol: unable to get usertype metatable"); - } + stack::stack_detail::undefined_metatable umf(L, &meta[0]); + umf(); - lua_setmetatable(L, -2); return 1; } @@ -508,7 +504,7 @@ namespace sol { typedef constructor_list F; static int call(lua_State* L, F&) { - const auto& metakey = usertype_traits::metatable(); + const auto& meta = usertype_traits::metatable(); int argcount = lua_gettop(L); call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits::user_metatable(), 1) : call_syntax::dot; argcount -= static_cast(syntax); @@ -519,13 +515,9 @@ namespace sol { construct_match(constructor_match(obj), L, argcount, boost + 1 + static_cast(syntax)); userdataref.push(); - luaL_getmetatable(L, &metakey[0]); - if (type_of(L, -1) == type::lua_nil) { - lua_pop(L, 1); - return luaL_error(L, "sol: unable to get usertype metatable"); - } + stack::stack_detail::undefined_metatable umf(L, &meta[0]); + umf(); - lua_setmetatable(L, -2); return 1; } }; @@ -537,7 +529,7 @@ namespace sol { struct onmatch { template int operator()(types, index_value, types r, types a, lua_State* L, int, int start, F& f) { - const auto& metakey = usertype_traits::metatable(); + const auto& meta = usertype_traits::metatable(); T* obj = detail::usertype_allocate(L); reference userdataref(L, -1); @@ -545,14 +537,8 @@ namespace sol { stack::call_into_lua(r, a, L, boost + start, func, detail::implicit_wrapper(obj)); userdataref.push(); - luaL_getmetatable(L, &metakey[0]); - if (type_of(L, -1) == type::lua_nil) { - lua_pop(L, 1); - std::string err = "sol: unable to get usertype metatable for "; - err += usertype_traits::name(); - return luaL_error(L, err.c_str()); - } - lua_setmetatable(L, -2); + stack::stack_detail::undefined_metatable umf(L, &meta[0]); + umf(); return 1; } @@ -579,11 +565,25 @@ namespace sol { struct lua_call_wrapper, is_index, is_variable, checked, boost, clean_stack, std::enable_if_t::value>> { typedef destructor_wrapper F; - static int call(lua_State* L, const F& f) { + static int call_void(std::true_type, lua_State* L, const F& f) { + typedef meta::bind_traits> bt; + typedef typename bt::template arg_at<0> arg0; + typedef meta::unqualified_t O; + + O& obj = stack::get(L); + f.fx(detail::implicit_wrapper(obj)); + return 0; + } + + static int call_void(std::false_type, lua_State* L, const F& f) { T& obj = stack::get(L); f.fx(detail::implicit_wrapper(obj)); return 0; } + + static int call(lua_State* L, const F& f) { + return call_void(std::is_void(), L, f); + } }; template diff --git a/sol/experimental_usertype.hpp b/sol/experimental_usertype.hpp new file mode 100644 index 00000000..d9ffacd4 --- /dev/null +++ b/sol/experimental_usertype.hpp @@ -0,0 +1,39 @@ +// sol2 + +// The MIT License (MIT) + +// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_EXPERIMENTAL_USERTYPE_HPP +#define SOL_EXPERIMENTAL_USERTYPE_HPP + +#include "table.hpp" + +namespace sol { + + template + struct metatable : basic_table { + + }; + + +} + +#endif SOL_EXPERIMENTAL_USERTYPE_HPP diff --git a/sol/function_types.hpp b/sol/function_types.hpp index dea692fa..1ee752df 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -431,6 +431,20 @@ namespace sol { lua_CFunction cf = call_detail::construct; return stack::push(L, cf); } + + static int push(lua_State* L, constructor_list) { + lua_CFunction cf = call_detail::construct; + return stack::push(L, cf); + } + }; + + template + struct pusher> { + typedef constructor_list cl_t; + static int push(lua_State* L, cl_t cl) { + typedef typename meta::bind_traits::return_type T; + return stack::push>(L, cl); + } }; template @@ -445,6 +459,16 @@ namespace sol { } }; + template + struct pusher> { + template + static int push(lua_State* L, C&& c) { + typedef typename meta::bind_traits::template arg_at<0> arg0; + typedef meta::unqualified_t> T; + return stack::push>>(L, std::forward(c)); + } + }; + template struct pusher>> { static int push(lua_State* L, destructor_wrapper) { @@ -455,11 +479,38 @@ namespace sol { template struct pusher>> { - static int push(lua_State* L, destructor_wrapper c) { + static int push(lua_State* L, destructor_wrapper&& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::push>(L, std::move(c)); + upvalues += stack::push>>(L, std::move(c)); + return stack::push(L, c_closure(cf, upvalues)); + } + + static int push(lua_State* L, const destructor_wrapper& c) { + lua_CFunction cf = call_detail::call_user, 2>; + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push>>(L, c); + return stack::push(L, c_closure(cf, upvalues)); + } + }; + + template + struct pusher> { + static int push(lua_State* L, destructor_wrapper&& c) { + lua_CFunction cf = call_detail::call_user, 2>; + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push>>(L, std::move(c)); + return stack::push(L, c_closure(cf, upvalues)); + } + + static int push(lua_State* L, const destructor_wrapper& c) { + lua_CFunction cf = call_detail::call_user, 2>; + int upvalues = 0; + upvalues += stack::push(L, nullptr); + upvalues += stack::push>>(L, c); return stack::push(L, c_closure(cf, upvalues)); } }; diff --git a/sol/load_result.hpp b/sol/load_result.hpp index 0d9dc803..1d396f31 100644 --- a/sol/load_result.hpp +++ b/sol/load_result.hpp @@ -122,13 +122,7 @@ namespace sol { template decltype(auto) call(Args&&... args) { -#if defined(_MSC_VER) && _MSC_VER == 1913 - // This compiler is bananas - // B, A N A N A S - return get().call(std::forward(args)...); -#else return get().template call(std::forward(args)...); -#endif } template diff --git a/sol/usertype_core.hpp b/sol/usertype_core.hpp index 4c941d68..821facc9 100644 --- a/sol/usertype_core.hpp +++ b/sol/usertype_core.hpp @@ -25,7 +25,6 @@ #define SOL_USERTYPE_CORE_HPP #include "wrapper.hpp" -#include "call.hpp" #include "stack.hpp" #include "types.hpp" #include "stack_reference.hpp" diff --git a/tests/test_plain_types.cpp b/tests/test_plain_types.cpp index 7570863b..77400410 100644 --- a/tests/test_plain_types.cpp +++ b/tests/test_plain_types.cpp @@ -92,4 +92,89 @@ TEST_CASE("plain/indestructible", "test that we error for types that are innatel auto result = lua.safe_script("collectgarbage()", sol::script_pass_on_error); REQUIRE(result.valid()); } -} \ No newline at end of file +} + +TEST_CASE("plain/constructors and destructors", "Make sure that constructors, destructors, deallocators and others work properly with the desired type") { + static int constructed = 0; + static int destructed = 0; + static int copied = 0; + static int moved = 0; + + struct st { + int value = 10; + + st() : value(10) { + ++constructed; + } + + st(const st& o) : value(o.value) { + ++copied; + ++constructed; + } + + st(st&& o) : value(o.value) { + ++moved; + ++constructed; + } + + ~st() { + value = 0; + ++destructed; + } + }; + + struct deallocate_only { + void operator()(st* p) const { + std::allocator alloc; + alloc.deallocate(p, 1); + } + }; + + { + sol::state lua; + + lua["f"] = sol::constructors(); + lua["g"] = sol::initializers([](st* mem) { std::allocator alloc; std::allocator_traits>::construct(alloc, mem); }); + lua["h"] = sol::factories([]() { return st(); }); + lua["d"] = sol::destructor([](st& m) { m.~st(); }); + + sol::protected_function_result result1 = lua.safe_script("v = f()", &sol::script_pass_on_error); + REQUIRE(result1.valid()); + st& v = lua["v"]; + REQUIRE(v.value == 10); + REQUIRE(constructed == 1); + REQUIRE(destructed == 0); + { + std::unique_ptr unsafe(new st()); + lua["unsafe"] = unsafe.get(); + sol::protected_function_result result2 = lua.safe_script("d(unsafe)", &sol::script_pass_on_error); + REQUIRE(result2.valid()); + REQUIRE(constructed == 2); + REQUIRE(destructed == 1); + } + REQUIRE(constructed == 2); + REQUIRE(destructed == 1); + + { + sol::protected_function_result result3 = lua.safe_script("vg = g()", &sol::script_pass_on_error); + REQUIRE(result3.valid()); + st& vg = lua["vg"]; + REQUIRE(vg.value == 10); + REQUIRE(constructed == 3); + REQUIRE(destructed == 1); + } + + { + sol::protected_function_result result4 = lua.safe_script("vh = h()", &sol::script_pass_on_error); + REQUIRE(result4.valid()); + st& vh = lua["vh"]; + REQUIRE(vh.value == 10); + } + } + int purely_constructed = constructed - moved - copied; + int purely_destructed = destructed - moved - copied; + REQUIRE(constructed == destructed); + REQUIRE(purely_constructed == purely_destructed); + REQUIRE(purely_constructed == 4); + REQUIRE(purely_destructed == 4); +}