From 006357430bbd18b01bce1af1b74272cece7251cb Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 23 Apr 2016 17:07:51 -0400 Subject: [PATCH] Shiny quick 'n' dirty tutorial -- variadic_args now is iterable -- fixed bug with transparent args in overload resolution -- added `load` functions --- docs/source/api/api-top.rst | 3 + docs/source/api/error.rst | 2 +- docs/source/api/property.rst | 58 ++++ docs/source/api/state.rst | 22 +- docs/source/api/this_state.rst | 31 ++ docs/source/api/usertype.rst | 20 +- docs/source/api/variadic_args.rst | 42 +++ docs/source/features.rst | 2 +- docs/source/index.rst | 1 + docs/source/tutorial/all-the-things.rst | 441 ++++++++++++++++++++++++ docs/source/tutorial/existing.rst | 2 +- docs/source/tutorial/functions.rst | 2 +- docs/source/tutorial/tutorial-top.rst | 7 +- sol/function_types_overload.hpp | 9 +- sol/protected_function.hpp | 4 +- sol/stack_proxy.hpp | 11 + sol/state_view.hpp | 11 + sol/traits.hpp | 6 + sol/variadic_args.hpp | 133 ++++++- test_functions.cpp | 6 +- tests.cpp | 3 +- 21 files changed, 782 insertions(+), 34 deletions(-) create mode 100644 docs/source/api/property.rst create mode 100644 docs/source/api/this_state.rst create mode 100644 docs/source/api/variadic_args.rst create mode 100644 docs/source/tutorial/all-the-things.rst diff --git a/docs/source/api/api-top.rst b/docs/source/api/api-top.rst index 8f5981c1..6ff62d7a 100644 --- a/docs/source/api/api-top.rst +++ b/docs/source/api/api-top.rst @@ -17,6 +17,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo protected_function object overload + property proxy reference resolve @@ -24,8 +25,10 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo optional state table + this_state thread types usertype userdata usertype_memory + variadic_args diff --git a/docs/source/api/error.rst b/docs/source/api/error.rst index f46bc06e..32677756 100644 --- a/docs/source/api/error.rst +++ b/docs/source/api/error.rst @@ -12,4 +12,4 @@ the single exception type 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 :doc:`nil`. -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. \ 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. \ No newline at end of file diff --git a/docs/source/api/property.rst b/docs/source/api/property.rst new file mode 100644 index 00000000..f25a9506 --- /dev/null +++ b/docs/source/api/property.rst @@ -0,0 +1,58 @@ +property +======== + +.. code-block:: cpp + + template + decltype(auto) property ( Read&& read_function, Write&& write_function ); + template + decltype(auto) property ( Read&& read_function ); + template + decltype(auto) property ( Write&& write_function ); + +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 + :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: + + lua.set("theplayer", Player()); + + 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 + + theplayer.hp = 20 + print(theplayer.hp) \ No newline at end of file diff --git a/docs/source/api/state.rst b/docs/source/api/state.rst index 88c2c021..bf178c96 100644 --- a/docs/source/api/state.rst +++ b/docs/source/api/state.rst @@ -30,6 +30,8 @@ enumerations debug, bit32, io, + ffi, + jit, count // do not use }; @@ -56,11 +58,19 @@ This function takes a number of :ref:`sol::lib` as arguments and opens These functions run the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. It will not run isolated: any scripts or code run will affect code in other states as well: code ran in this fashion is not isolated. +.. code-block:: cpp + :caption: function: load / load_file + + sol::stack_proxy load(const std::string& code); + sol::stack_proxy load_file(const std::string& filename); + +These functions *load* the desired blob of either code that is in a string, or code that comes from a filename, on the ``lua_State*``. It will not run: it returns a proxy that can be called, turned into a `sol::function`,. or similar, will run the loaded code. + .. code-block:: cpp :caption: function: global table / registry table - global_table globals() const; - table registry() const; + sol::global_table globals() const; + sol::table registry() const; Get either the global table or the Lua registry as a :doc:`sol::table`, which allows you to modify either of them directly. Note that getting the global table from a ``state``/``state_view`` is usually unnecessary as it has all the exact same functions as a :doc:`sol::table
` anyhow. @@ -76,13 +86,13 @@ Overrides the panic function Lua calls when something unrecoverable or unexpecte .. code-block:: cpp :caption: function: make a table - table create_table(int narr = 0, int nrec = 0); + sol::table create_table(int narr = 0, int nrec = 0); template - table create_table(int narr, int nrec, Key&& key, Value&& value, Args&&... args); + sol::table create_table(int narr, int nrec, Key&& key, Value&& value, Args&&... args); - static table create_table(lua_State* L, int narr = 0, int nrec = 0); + static sol::table create_table(lua_State* L, int narr = 0, int nrec = 0); template - static table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args); + static sol::table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args); Creates a table. Forwards its arguments to :ref:`table::create`. diff --git a/docs/source/api/this_state.rst b/docs/source/api/this_state.rst new file mode 100644 index 00000000..c4c6b3b7 --- /dev/null +++ b/docs/source/api/this_state.rst @@ -0,0 +1,31 @@ +this_state +========== +transparent state argument for the current state +------------------------------------------------ + +.. code-block:: cpp + + struct this_state; + +This class is a transparent type that is meant to be gotten in functions to get the current lua state a bound function or usertype method is being called from. It does not actually retrieve anything from lua nor does it increment the argument count, making it "invisible" to function calls in lua and calls through ``std::function<...>`` and :doc:`sol::function` on this type. It can be put in any position in the argument list of a function: + +.. code-block:: cpp + :linenos: + + sol::state lua; + + lua.set_function("bark", []( sol::this_state s, int a, int b ){ + lua_State* L = s; // current state + return a + b + lua_gettop(L); + }); + + lua.script("first = bark(2, 2)"); // only takes 2 arguments, NOT 3 + + // Can be at the end, too, or in the middle: doesn't matter + lua.set_function("bark", []( int a, int b, sol::this_state s ){ + lua_State* L = s; // current state + return a + b + lua_gettop(L); + }); + + lua.script("second = bark(2, 2)"); // only takes 2 arguments + \ No newline at end of file diff --git a/docs/source/api/usertype.rst b/docs/source/api/usertype.rst index a05d09c0..b654b357 100644 --- a/docs/source/api/usertype.rst +++ b/docs/source/api/usertype.rst @@ -146,9 +146,9 @@ The constructor of usertype takes a variable number of arguments. It takes an ev * ``"{name}", constructors`` - ``Type-List-N`` must be a ``sol::types``, where ``Args...`` is a list of types that a constructor takes. Supports overloading by default - If you pass the ``constructors<...>`` argument first when constructing the usertype, then it will automatically be given a ``"{name}"`` of ``"new"`` -* ``"{name}", initializers( func1, func2, ... )`` +* ``"{name}", sol::initializers( func1, func2, ... )`` - Creates initializers that, given one or more functions, provides an overloaded lua function for creating a the specified type. - + The function must have the argument signature ``func T*, Arguments... )`` or ``func( T&, Arguments... )``, where the pointer or reference will point to a place of allocated memory that has an unitialized ``T``. Note that lua controls the memory. + + The function must have the argument signature ``func( T*, Arguments... )`` or ``func( T&, Arguments... )``, where the pointer or reference will point to a place of allocated memory that has an uninitialized ``T``. Note that lua controls the memory. .. _destructor: @@ -157,9 +157,15 @@ The constructor of usertype takes a variable number of arguments. It takes an ev - If you just want the default constructor, you can replace the second argument with ``sol::default_destructor``. - The usertype will throw if you specify a destructor specifically but do not map it to ``sol::meta_function::gc`` or a string equivalent to ``"__gc"``. * ``"{name}", &free_function`` - - Binds a free function / static class function / function object (lambda) to ``"{name}"``. The first argument must be ``T*`` or ``T&`` in this case. -* ``"{name}", &type::function_name`` or ``"{name}", &type::member_variable`` + - Binds a free function / static class function / function object (lambda) to ``"{name}"``. If the first argument is ``T*`` or ``T&``, then it will bind it as a member function. If it is not, it will be bound as a "static" function on the lua table. +* ``"{name}", &type::function_name`` or ``"{name}", &type::member_variable`` - Binds a typical member function or variable to ``"{name}"``. In the case of a member variable or member function, ``type`` must be ``T`` or a base of ``T``. +* ``"{name}", sol::readonly( &type::member_variable )`` + - Binds a typical variable to ``"{name}"``. Similar to the above, but the variable will be read-only, meaning an error will be generated if anything attemps to write to this variable. +* ``"{name}", sol::property( &type::getter_func, &type::setter_func )`` + - Binds a typical variable to ``"{name}"``, but gets and sets using the specified setter and getter functions. Not that if you do not pass a setter function, the variable will be read-only. Also not that if you do not pass a getter function, it will be write-only. +* ``"{name}", sol::overloaded( Func1, Func2, ... )`` + - Creates an oveloaded member function that discriminates on number of arguments and types. * ``sol::base_classes, sol::bases`` - Tells a usertype what its base classes are. If you have exceptions turned on, this need not be necessary: if you do not then you need this to have derived-to-base conversions work properly. See :ref:`inheritance`. @@ -198,7 +204,7 @@ You must specify the ``sol::base_classes`` tag with the ``sol::bases() }; struct B : A { int b = 11; - virtual int call() { return 20; } + virtual int call() override { return 20; } }; Then, to register the base classes explicitly: @@ -222,10 +228,6 @@ inheritance + overloading While overloading is supported regardless of `inheritance` caveats or not, the current version of Sol has a first-match, first-call style of overloading when it comes to inheritance. Put the functions with the most derived arguments first to get the kind of matching you expect. -.. todo:: - - Later versions of Sol will introduce a kind of overload resolution system that will rank overloads and call the best one, which will unfortunately come at a small performance penalty if you explicitly use overloads of many functions that have the same arity (argument count). - traits ------ diff --git a/docs/source/api/variadic_args.rst b/docs/source/api/variadic_args.rst new file mode 100644 index 00000000..b7925ffc --- /dev/null +++ b/docs/source/api/variadic_args.rst @@ -0,0 +1,42 @@ +variadic_args +============= +transparent argument to deal with multiple parameters to a function +------------------------------------------------------------------- + + +.. code-block:: cpp + + struct variadic_args; + +This class is meant to represent every single argument at its current index and beyond in a function list. It does not increment the argument count and is thus transparent. You can place it anyway in the argument list, and it will represent all of the objects in a function call that come after it, whether they are listed explicitly or not. + +.. code-block:: cpp + :linenos: + + sol::state lua; + + // Function requires 2 arguments + // rest can be variadic, but: + // va will include everything after "a" argument, + // which means "b" will be part of the varaidic_args list too + // at position 0 + lua.set_function("v", [](sol::this_state, int a, sol::variadic_args va, int b) { + int r = 0; + for (auto v : va) { + int value = v; // get argument out (implicit conversion) + // can also do int v = va.get(i); with index i + r += value; + } + // Only have to add a, b was included + return r + a; + }); + + lua.script("x = v(25, 25)"); + lua.script("x2 = v(25, 25, 100, 50, 250, 150)"); + lua.script("x3 = v(1, 2, 3, 4, 5, 6)"); + // will error: not enough arguments + //lua.script("x4 = v(1)"); + + lua.script("print(x)"); // 50 + lua.script("print(x2)"); // 600 + lua.script("print(x3)"); // 21 diff --git a/docs/source/features.rst b/docs/source/features.rst index 77e360bc..21f8d841 100644 --- a/docs/source/features.rst +++ b/docs/source/features.rst @@ -30,6 +30,7 @@ what Sol supports * User-Defined Type (:doc:`sol::usertype` in the API) support: - Set member functions to be called - Set member variables + - Set variables on a class that are based on setter/getter functions - Use free-functions that take the Type as a first argument (pointer or reference) - Support for "Factory" classes that do not expose constructor or destructor - Modifying memory of userdata in C++ directly affects Lua without copying, and @@ -178,7 +179,6 @@ Selene - * Eats crap when it comes to performance, most of the time (see :doc:`benchmarks`) * Lots of users, but the Repository is kinda stagnant... - luawrapper - * Takes the approach of writing and reading tables using ``readVariable`` and ``writeVariable`` functions diff --git a/docs/source/index.rst b/docs/source/index.rst index 35b813a4..08eb4289 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,6 +22,7 @@ get going: :name: mastertoc tutorial/tutorial-top + tutorial/all-the-things api/api-top features benchmarks diff --git a/docs/source/tutorial/all-the-things.rst b/docs/source/tutorial/all-the-things.rst new file mode 100644 index 00000000..85551dd1 --- /dev/null +++ b/docs/source/tutorial/all-the-things.rst @@ -0,0 +1,441 @@ +quick 'n' dirty - all the things +================================ + +These are all the things. Use your browser's search to find something that might help. + +Compile with -std=c++14 or better / VS 2015 or better. + +opening a state +--------------- + +Do it. + +.. code-block:: cpp + + sol::state lua; + // open some common libraries + lua.open_libraries(sol::lib::base, sol::lib::package); + lua.script( "print('bark bark bark!')" ); + + +sol::state on lua_State* +------------------------ + +For your system/game that already has lua, but you'd like something nice: + +.. code-block:: cpp + + int pre_existing_system( lua_State* L ) { + sol::state_view lua(L); + lua.script( "print('bark bark bark!')" ); + } + + +running lua code +---------------- + + sol::state lua; + // load and execute from string + lua.script("a = 'test'"); + // load and execute from file + lua.script_file("path/to/luascript.lua"); + + // load file without execute + sol::function script1 = state.load_script_file("path/to/luascript.lua"); + script1(); //execute + // load string without execute + sol::function script2 = state.load_script("a = 'test'"); + script2(); //execute + + +set and get stuff +----------------- + +You can set/get everything. + +.. code-block:: cpp + + sol::lua_state lua; + + lua.set("number", 24); // integer types + lua["number2"] = 24.5; // floating point numbers + lua["important_string"] = "woof woof"; // becomes string + lua["myuserdata"] = some_class(); // non-recognized types is stored as userdata + lua["a_function"] = [](){ return 100; }; // is callable, therefore gets stored as a function + + int number = lua["number"]; // implicit conversion + auto number2 = lua.get("number2"); // explicit get + std::string important_string = lua["important_string"]; // strings too + + some_class& myuserdata = lua["myuserdata"]; // returns a plain reference + // myuserdata.some_variable = 20 WILL (!!) modify + // data inside of lua VM as well, if you get a pointer or a reference + + // get a function + sol::function a_function = lua["a_function"]; + int value_is_100 = a_function(); + + // get a std::function + std::function a_std_function = lua["a_function"]; + int value_is_still_100 = a_std_function(); + + +Some classes that have stuff to make it easier to look at lua semantics / be safe. + +.. code-block:: cpp + + sol::state lua; + + // ... everything from before + + sol::object number_obj = lua.get( "number" ); + sol::type t1 = number_obj.get_type(); // sol::type::number + + sol::object function_obj = lua[ "a_function" ]; + sol::type t2 = function_obj.get_type(); // sol::type::function + bool is_it_really = function_obj.is(); // true + + // will not contain data + sol::optional check_for_me = lua["a_function"]; + + +functions +--------- + +They're great. Use them: + +.. code-block:: cpp + + sol::state lua; + + lua.script("function f (a, b, c, d) return 1 end"); + std::function stdfx = lua["f"]; + sol::function fx = lua["f"]; + + int is_one = stdfx(1, 34.5, 3, "bark"); + int is_also_one = fx(); + +You can bind member variables as functions too: + +.. code-block:: cpp + + void some_function () { + std::cout << "some function!" << std::endl; + } + + struct some_class { + int variable = 30; + + double member_function () { + return 24.5; + } + }; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua["f1"] = some_function; + lua.set_function("f2", &some_other_function); + + lua.script(R"( + f1() -- some function! + f2() -- some function! + )"); + + lua.set("sc", some_class()); // put an instance of "some_class" into lua + + lua["m1"] = &some_class::member_function; // binds just the member function + lua.set_function("m2", &some_class::member_function, some_class{}); // binds the class to the type + + lua.script(R"( + -- need class instance if you don't bind it with the function + print(m1(sc)) -- 24.5 + -- does not need class instance: was made with one + print(m2()) -- 24.5 + )"); + + lua["v1"] = &some_class::variable; // binds just the membver variable as a function + lua.set_function("v2", &some_class::variable, some_class{}); // binds class with member variable as function + + lua.script(R"( + -- need class instance if you don't bind it with the function + print(v1(sc)) -- 30 + -- does not need class instance: was bound with one + print(v2()) -- 30 + + -- can set: still requires instance + v1(sc, 212) + -- can set: does not need class instance: was bound with one + v2(254) + + print(v1(sc)) -- 212 + print(v2()) -- 254 + )"); + +Can use ``sol::readonly( &some_class::variable )`` to make a variable readonly and error if someone tries to write to it. + + +multiple returns +---------------- + +.. code-block:: cpp + + sol::state lua; + + lua.script("function f (a, b, c) return a, b, c end"); + + std::tuple result = lua["f"](100, 200, 300); + // result == { 100, 200, 300 } + int a, int b; + std::string c; + sol::bond( a, b, c ) = lua["f"](100, 200, "bark"); + // a == 100 + // b == 200 + // c == "bark" + + +tables +------ + +:doc:`state<../api/state>` is a table too. + +.. code-block:: cpp + + sol::state lua; + + // Raw string literal for easy multiline + lua.script( R"( + abc = { [0] = 24 } + def = { + ghi = { + bark = 50, + woof = abc + } + } + )" + ); + + sol::table abc = lua["abc"]; + sol::table ghi = lua["def"]["ghi"]; + + int bark1 = def["y"]["bark"]; // 24 + int bark2 = lua["def"]["ghi"]["bark"]; // 24 + bool bark_equal = bark1 == bark2; // true + + int abcval1 = abc[0]; // 24 + int abcval2 = ghi["woof"][0]; // 24 + bool abcval_equal = abcval1 == abcval2; // true + +If you're going deep, be safe: + +.. code-block:: cpp + + sol::optional will_not_error = lua["abc"]["DOESNOTEXIST"]["ghi"]; // sol::nullopt + int will_not_error2 = lua["abc"]["def"]["ghi"]["jklm"].get_or(25); // is 25 + // will throw (or do at_panic if no exceptions) + int aaaahhh = lua["abc"]["hope_u_liek_crash"]; + + +make tables +----------- + +Make some: + +.. code-block:: cpp + + lua["abc"] = lua.create_table_with( + 0, 24 + ); + + lua.create_named_table("def", + "ghi", lua.create_table_with( + "bark", 50, + "woof", lua["abc"] // can reference other existing stuff too + ) + ); + +Equivalent Lua code: + +.. code-block:: lua + + abc = { [0] = 24 } + def = { + ghi = { + bark = 50, + woof = abc + } + } + + +userdata + usertypes (metatables) +--------------------------------- + +Everything that is not a: + + * primitive type: ``bool``, ``char/short/int/long/long long``, ``float/double`` + * string type: ``std::string``, ``const char*`` + * function type: function pointers, ``lua_CFunction``, ``std::function``, :doc:`sol::function/sol::protected_function<../api/function>`, :doc:`sol::coroutine<../api/coroutine>` + * designated sol type: :doc:`sol::table<../api/table>`, :doc:`sol::thread<../api/thread>`, :doc:`sol::error<../api/error>`, :doc:`sol::object<../api/object>` + * transparent argument type: :doc:`sol::variadic_arg<../api/variadic_args>`, :doc:`sol::this_state<../api/this_state>` + * usertype class: :doc:`sol::usertype<../api/usertype>` + +Is set as a userdata. + +.. code-block:: cpp + + struct Doge { int tailwag = 50; } + + Doge dog{}; + + // Copy into lua: destroyed when lua VM garbage collects + lua["dog"] = dog; + // OR: move semantics - will call move constructor if present instead + lua["dog"] = std::move( dog ); + lua["dog"] = Doge{}; + lua["dog"] = std::make_unique(); + lua["dog"] = std::make_shared(); + // Identical to above + lua.set("dog", dog); + lua.set("dog", std::move(dog)); + lua.set("dog", Doge{}); + lua.set("dog", std::unique_ptr(new Doge())); + lua.set("dog", std::shared_ptr(new Doge())); + +``std::unique_ptr``/``std::shared_ptr``'s reference counts / deleters will be respected. If you want it to refer to something, whose memory you know won't die in C++, do the following: + +.. code-block:: cpp + + Doge dog{}; // Kept alive somehow + + // Later... + // The following stores a reference, and does not copy/move + // lifetime is same as dog in C++ (access after it is destroyed is bad) + lua["dog"] = &dog; + // Same as above: respects std::reference_wrapper + lua["dog"] = std::ref(dog); + // These two are identical to above + lua.set( "dog", &dog ); + lua.set( "dog", std::ref( dog ) ); + +Get userdata in the same way as everything else: + +.. code-block:: cpp + + Doge& dog = lua["dog"]; // References Lua memory + Doge* dog_pointer = lua["dog"]; // References Lua memory + Doge dog_copy = lua["dog"]; // Copies, will not affect lua + + dog_copy.tailwag = 525; + // Still 50 + lua.script("assert(dog.tailwag == 50)"); + + dog.tailwag = 100; + // Now 100 + lua.script("assert(dog.tailwag == 100)"); + + +more userdata + usertypes +------------------------- + +Because there's a LOT you can do with Sol: + +.. code-block:: cpp + :caption: test_player.hpp + + struct player { + public: + int bullets; + int speed; + + player() : player(500, 100) { + + } + + player(int ammo) : player(ammo, 100) { + + } + + player(int ammo, int hitpoints) : bullets(ammo), hp(hitpoints) { + + } + + void boost () { + speed += 10; + } + + bool shoot () { + if (bullets < 1) + return false; + --bullets; + return true; + } + + int set_hp(int value) { + hp = value; + } + + int get_hp() const { + return hp; + } + + private: + int hp; + } + +Bind all the things: + +.. code-block:: cpp + :caption: player_script.cpp + + sol::state lua; + + // just stuff a userdata in there + lua.new_usertype( "player", + sol::constructors, sol::types, sol::types>(), // 3 constructors + "shoot", &player::shoot, // typical member function that returns a variable + "boost", &player::boost, // typical member function + "hp", sol::property(&player::get_hp, &player::set_hp), // gets or set the value + "speed", &player::speed, // read and write variable + "bullets", sol::readonly( &player::bullets ) // can only read from, not write to + ); + + lua.script_file("player_script.lua"); + +And the script: + +.. code-block:: lua + :caption: player_script.lua + + p1 = player.new(2) -- call single argument integer constructor + + p1.hp = 545; -- call property setter + print(p1.hp); -- call property through getter + + local did_shoot_1 = p1:shoot() + print(did_shoot_1) + print(p1.bullets) + local did_shoot_2 = p1:shoot() + print(did_shoot_2) + print(p1.bullets) + local did_shoot_3 = p1:shoot() + print(did_shoot_3) + + -- can read + print(p1.bullets) + -- would error: is a readonly variable, cannot write + -- p1.bullets = 20 + + p1:boost() + +Even more stuff :doc:`you can do<../api/usertype>` described elsewhere, like initializer functions (private constructors / destructors support), "static" functions callable with ``name.my_function( ... )``, and overloaded member functions. + + +Advanced +-------- + +Some more advanced things you can do: + + * :doc:`stack manipulation<../api/stack>` to safely play with the stack. You can also define customization points for ``stack::get``/``stack::check``/``stack::push`` for your type. + * :doc:`variadic arguments<../api/variadic_args>` in functions with ``sol::variadic_args``. + * :doc:`this_state<../api/this_state>` to get the current ``lua_State*``. + * :doc:`resolve<../api/resolve>` overloads in case you have overloaded functions; a cleaner casting utility. \ No newline at end of file diff --git a/docs/source/tutorial/existing.rst b/docs/source/tutorial/existing.rst index 4b018abd..9b77fb90 100644 --- a/docs/source/tutorial/existing.rst +++ b/docs/source/tutorial/existing.rst @@ -18,6 +18,6 @@ If you're already using lua and you just want to use ``sol`` in some places, you // start using it... } -:doc:`sol::state_view<../api/state` is exactly like ``sol::state``, but it doesn't manage the lifetime of a ``lua_State*``. Therefore, you get all the goodies that come with a ``sol::state`` without any of the ownership implications. Sol has no initialization components that need to deliberately remain alive for the duration of the program. It's entirely self-containing and uses lua's garbage collectors and various implementation techniques to require no state C++-side. After you do that, all of the power of `Sol` is available to you, and then some! +:doc:`sol::state_view<../api/state>` is exactly like ``sol::state``, but it doesn't manage the lifetime of a ``lua_State*``. Therefore, you get all the goodies that come with a ``sol::state`` without any of the ownership implications. Sol has no initialization components that need to deliberately remain alive for the duration of the program. It's entirely self-containing and uses lua's garbage collectors and various implementation techniques to require no state C++-side. After you do that, all of the power of `Sol` is available to you, and then some! Remember that Sol can be as lightweight as you want it: almost all of Sol's types take the ``lua_State*`` argument and then a second ``int index`` stack index argument, meaning you can use :doc:`tables<../api/table>`, :doc:`lua functions<../api/function>`, :doc:`coroutines<../api/coroutine>`, and other reference-derived objects that expose the proper constructor for your use. You can also set :doc:`usertypes<../api/usertype>` and other things you need without changing your entire architecture! \ No newline at end of file diff --git a/docs/source/tutorial/functions.rst b/docs/source/tutorial/functions.rst index 81d3395a..20975c82 100644 --- a/docs/source/tutorial/functions.rst +++ b/docs/source/tutorial/functions.rst @@ -1,4 +1,4 @@ functions and You ================= -Sol can register all kinds of functions. [ WIP - Check back soon! ] \ No newline at end of file +Sol can register all kinds of functions. [ WIP - Check back soon. Until its done, use the :doc:`quick 'n' dirty` and the :doc:`api<../api/api-top>` to get going! ] \ No newline at end of file diff --git a/docs/source/tutorial/tutorial-top.rst b/docs/source/tutorial/tutorial-top.rst index b03475d5..f859e0fa 100644 --- a/docs/source/tutorial/tutorial-top.rst +++ b/docs/source/tutorial/tutorial-top.rst @@ -1,7 +1,7 @@ -get going FAST -============== +Tutorial +======== -Ride into the speed lane by learning the all the ins-and-outs of Sol with these tutorials! You'll be up and running in minutes, maybe even faster if you're just That Good™. +Take some time to learn the framework with thse tutorials. But, if you need to get going FAST, try using the :doc:`quick 'n' dirty` approach and your browser's / editors search function. .. toctree:: @@ -9,6 +9,7 @@ Ride into the speed lane by learning the all the ins-and-outs of Sol with these :name: tutorialtoc :maxdepth: 1 + all-the-things getting-started existing variables diff --git a/sol/function_types_overload.hpp b/sol/function_types_overload.hpp index 5157c6c9..8f394a91 100644 --- a/sol/function_types_overload.hpp +++ b/sol/function_types_overload.hpp @@ -50,12 +50,15 @@ inline int overload_match_arity(types, std::index_sequence typedef meta::tuple_types return_types; typedef typename traits::args_type args_type; typedef typename args_type::indices args_indices; + typedef meta::index_in state_index; + typedef meta::index_in va_pack_index; + static const std::size_t arity = traits::arity - static_cast(state_index::value != SIZE_MAX) - static_cast(va_pack_index::value != SIZE_MAX); // compile-time eliminate any functions that we know ahead of time are of improper arity - if (meta::find_in_pack_v, index_value...>::value) { + if (meta::find_in_pack_v, index_value...>::value) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } - if (traits::arity != fxarity) { - return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); + if (arity != fxarity) { + return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!stack::stack_detail::check_types().check(args_type(), args_indices(), L, start, no_panic)) { return overload_match_arity(types(), std::index_sequence(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index 0de58c87..b2cc7561 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -135,7 +135,9 @@ public: basic_protected_function& operator=(const basic_protected_function&) = default; basic_protected_function(basic_protected_function&& ) = default; basic_protected_function& operator=(basic_protected_function&& ) = default; - basic_protected_function(lua_State* L, int index = -1): base_t(L, index), error_handler(get_default_handler()) { + basic_protected_function(const basic_function& b) : base_t(b) {} + basic_protected_function(basic_function&& b) : base_t(std::move(b)) {} + basic_protected_function(lua_State* L, int index = -1) : base_t(L, index), error_handler(get_default_handler()) { #ifdef SOL_CHECK_ARGUMENTS stack::check(L, index, type_panic); #endif // Safety diff --git a/sol/stack_proxy.hpp b/sol/stack_proxy.hpp index 76935fae..47249954 100644 --- a/sol/stack_proxy.hpp +++ b/sol/stack_proxy.hpp @@ -32,6 +32,7 @@ private: int index; public: + stack_proxy() : L(nullptr), index(0){} stack_proxy(lua_State* L, int index) : L(L), index(index) {} template @@ -46,6 +47,16 @@ public: lua_State* lua_state() const { return L; } int stack_index() const { return index; } + + template + decltype(auto) call(Args&&... args) { + return get().template call(std::forward(args)...); + } + + template + decltype(auto) operator()(Args&&... args) { + return call<>(std::forward(args)...); + } }; namespace stack { diff --git a/sol/state_view.hpp b/sol/state_view.hpp index d0c99d08..a052f439 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -24,6 +24,7 @@ #include "error.hpp" #include "table.hpp" +#include "stack_proxy.hpp" #include namespace sol { @@ -157,6 +158,16 @@ public: } } + stack_proxy load(const std::string& code) { + luaL_loadstring(L, code.c_str()); + return stack_proxy(L, -1); + } + + stack_proxy load_file(const std::string& filename) { + luaL_loadfilex(L, filename.c_str(), nullptr); + return stack_proxy(L, -1); + } + iterator begin () const { return global.begin(); } diff --git a/sol/traits.hpp b/sol/traits.hpp index e1eed967..792ee0ae 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -143,6 +143,12 @@ namespace meta_detail { template struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { }; +template +struct index_in : meta_detail::index_in_pack<0, T, List> { }; + +template +struct index_in> : meta_detail::index_in_pack<0, T, Args...> { }; + template struct at_in_pack {}; diff --git a/sol/variadic_args.hpp b/sol/variadic_args.hpp index 6a4bef01..db228aa3 100644 --- a/sol/variadic_args.hpp +++ b/sol/variadic_args.hpp @@ -24,8 +24,109 @@ #include "stack.hpp" #include "stack_proxy.hpp" +#include namespace sol { + struct va_iterator : std::iterator { + lua_State* L; + int index; + int stacktop; + stack_proxy sp; + + va_iterator() : L(nullptr), index(std::numeric_limits::max()), stacktop(std::numeric_limits::max()) {} + va_iterator(lua_State* L, int index, int stacktop) : L(L), index(index), stacktop(stacktop), sp(L, index) {} + + stack_proxy operator*() { + sp = stack_proxy(L, index); + return sp; + } + + stack_proxy* operator->() { + sp = stack_proxy(L, index); + return &sp; + } + + va_iterator& operator++ () { + ++index; + return *this; + } + + va_iterator operator++ (int) { + auto r = *this; + this->operator ++(); + return r; + } + + va_iterator& operator-- () { + --index; + return *this; + } + + va_iterator operator-- (int) { + auto r = *this; + this->operator --(); + return r; + } + + va_iterator& operator+= (difference_type idx) { + index += static_cast(idx); + return *this; + } + + va_iterator& operator-= (difference_type idx) { + index -= static_cast(idx); + return *this; + } + + difference_type operator- (const va_iterator& r) const { + return index - r.index; + } + + va_iterator operator+ (difference_type idx) const { + va_iterator r = *this; + r += idx; + return r; + } + + stack_proxy operator[](difference_type idx) { + return stack_proxy(L, index + static_cast(idx)); + } + + bool operator==(const va_iterator& r) const { + if (stacktop == std::numeric_limits::max()) { + return r.index == r.stacktop; + } + else if (r.stacktop == std::numeric_limits::max()) { + return index == stacktop; + } + return index == r.index; + } + + bool operator != (const va_iterator& r) const { + return !(this->operator==(r)); + } + + bool operator < (const va_iterator& r) const { + return index < r.index; + } + + bool operator > (const va_iterator& r) const { + return index > r.index; + } + + bool operator <= (const va_iterator& r) const { + return index <= r.index; + } + + bool operator >= (const va_iterator& r) const { + return index >= r.index; + } + }; + + inline va_iterator operator+(typename va_iterator::difference_type n, const va_iterator& r) { + return r + n; + } + struct variadic_args { private: lua_State* L; @@ -33,6 +134,16 @@ private: int stacktop; public: + typedef stack_proxy reference_type; + typedef stack_proxy value_type; + typedef stack_proxy* pointer; + typedef std::ptrdiff_t difference_type; + typedef std::size_t size_type; + typedef va_iterator iterator; + typedef const va_iterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + variadic_args() = default; variadic_args(lua_State* L, int index = -1): L(L), index(lua_absindex(L, index)), stacktop(lua_gettop(L)) {} variadic_args(const variadic_args&) = default; @@ -58,6 +169,20 @@ public: return *this; } + iterator begin() { return va_iterator(L, index, stacktop + 1); } + iterator end() { return va_iterator(L, stacktop + 1, stacktop + 1); } + const_iterator begin() const { return va_iterator(L, index, stacktop + 1); } + const_iterator end() const { return va_iterator(L, stacktop + 1, stacktop + 1); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + reverse_iterator rbegin() { return std::make_reverse_iterator(begin()); } + reverse_iterator rend() { return std::make_reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return std::make_reverse_iterator(begin()); } + const_reverse_iterator rend() const { return std::make_reverse_iterator(end()); } + const_reverse_iterator crbegin() const { return std::make_reverse_iterator(cbegin()); } + const_reverse_iterator crend() const { return std::make_reverse_iterator(cend()); } + int push () const { int pushcount = 0; for (int i = index; i <= stacktop; ++i) { @@ -68,12 +193,12 @@ public: } template - decltype(auto) get(int start = 0) const { - return stack::get(L, index + start); + decltype(auto) get(difference_type start = 0) const { + return stack::get(L, index + static_cast(start)); } - stack_proxy operator[](int start) const { - return stack_proxy(L, index + start); + stack_proxy operator[](difference_type start) const { + return stack_proxy(L, index + static_cast(start)); } lua_State* lua_state() const { return L; }; diff --git a/test_functions.cpp b/test_functions.cpp index 312816e9..65fb36f9 100644 --- a/test_functions.cpp +++ b/test_functions.cpp @@ -760,9 +760,9 @@ TEST_CASE("functions/variadic_args", "Check to see we can receive multiple argum lua.open_libraries(sol::lib::base); lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure { int r = 0; - for (int i = 0; i < va.leftover_count(); ++i) { - int v = va[i]; - r += v; + for (auto v : va) { + int value = v; + r += value; } return{ r, r > 200 }; }); diff --git a/tests.cpp b/tests.cpp index 3b416a33..042e18fd 100644 --- a/tests.cpp +++ b/tests.cpp @@ -191,7 +191,8 @@ TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down sol::state lua; int begintop = 0, endtop = 0; - lua.script("t1 = {t2 = {t3 = 24}};"); + sol::function scriptload = lua.load("t1 = {t2 = {t3 = 24}};"); + scriptload(); { test_stack_guard g(lua.lua_state(), begintop, endtop); int traversex24 = lua.traverse_get("t1", "t2", "t3");