Shiny quick 'n' dirty tutorial -- variadic_args now is iterable -- fixed bug with transparent args in overload resolution -- added load functions

This commit is contained in:
ThePhD 2016-04-23 17:07:51 -04:00
parent 69ad2b4c71
commit 006357430b
21 changed files with 782 additions and 34 deletions

View File

@ -17,6 +17,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
protected_function protected_function
object object
overload overload
property
proxy proxy
reference reference
resolve resolve
@ -24,8 +25,10 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
optional optional
state state
table table
this_state
thread thread
types types
usertype usertype
userdata userdata
usertype_memory usertype_memory
variadic_args

View File

@ -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<set-panic>`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil<types>`. 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<set-panic>`. If you turn :doc:`off exceptions<../exceptions>`, the chances of you seeing this error are :doc:`nil<types>`.
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. 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.

View File

@ -0,0 +1,58 @@
property
========
.. code-block:: cpp
template <typename Read, typename Write>
decltype(auto) property ( Read&& read_function, Write&& write_function );
template <typename Read>
decltype(auto) property ( Read&& read_function );
template <typename Write>
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<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>( "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)

View File

@ -30,6 +30,8 @@ enumerations
debug, debug,
bit32, bit32,
io, io,
ffi,
jit,
count // do not use count // do not use
}; };
@ -56,11 +58,19 @@ This function takes a number of :ref:`sol::lib<lib-enum>` 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. 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 .. code-block:: cpp
:caption: function: global table / registry table :caption: function: global table / registry table
global_table globals() const; sol::global_table globals() const;
table registry() const; sol::table registry() const;
Get either the global table or the Lua registry as a :doc:`sol::table<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<table>` anyhow. Get either the global table or the Lua registry as a :doc:`sol::table<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<table>` anyhow.
@ -76,13 +86,13 @@ Overrides the panic function Lua calls when something unrecoverable or unexpecte
.. code-block:: cpp .. code-block:: cpp
:caption: function: make a table :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 <typename Key, typename Value, typename... Args> template <typename Key, typename Value, typename... Args>
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 <typename Key, typename Value, typename... Args> template <typename Key, typename Value, typename... Args>
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<table-create>`. Creates a table. Forwards its arguments to :ref:`table::create<table-create>`.

View File

@ -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<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

View File

@ -146,9 +146,9 @@ The constructor of usertype takes a variable number of arguments. It takes an ev
* ``"{name}", constructors<Type-List-0, Type-List-1, ...>`` * ``"{name}", constructors<Type-List-0, Type-List-1, ...>``
- ``Type-List-N`` must be a ``sol::types<Args...>``, where ``Args...`` is a list of types that a constructor takes. Supports overloading by default - ``Type-List-N`` must be a ``sol::types<Args...>``, 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"`` - 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. - 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: .. _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``. - 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"``. - 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`` * ``"{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. - 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`` * ``"{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``. - 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<Bases...>`` * ``sol::base_classes, sol::bases<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<usertype-inheritance>`. - 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<usertype-inheritance>`.
@ -198,7 +204,7 @@ You must specify the ``sol::base_classes`` tag with the ``sol::bases<Types...>()
}; };
struct B : A { struct B : A {
int b = 11; int b = 11;
virtual int call() { return 20; } virtual int call() override { return 20; }
}; };
Then, to register the base classes explicitly: Then, to register the base classes explicitly:
@ -222,10 +228,6 @@ inheritance + overloading
While overloading is supported regardless of `inheritance<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. While overloading is supported regardless of `inheritance<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 traits
------ ------

View File

@ -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<int>(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

View File

@ -30,6 +30,7 @@ what Sol supports
* User-Defined Type (:doc:`sol::usertype<api/usertype>` in the API) support: * User-Defined Type (:doc:`sol::usertype<api/usertype>` in the API) support:
- Set member functions to be called - Set member functions to be called
- Set member variables - 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) - 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 - Support for "Factory" classes that do not expose constructor or destructor
- Modifying memory of userdata in C++ directly affects Lua without copying, and - 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<benchmarks>`) * Eats crap when it comes to performance, most of the time (see :doc:`benchmarks<benchmarks>`)
* Lots of users, but the Repository is kinda stagnant... * Lots of users, but the Repository is kinda stagnant...
luawrapper - luawrapper -
* Takes the approach of writing and reading tables using ``readVariable`` and ``writeVariable`` functions * Takes the approach of writing and reading tables using ``readVariable`` and ``writeVariable`` functions

View File

@ -22,6 +22,7 @@ get going:
:name: mastertoc :name: mastertoc
tutorial/tutorial-top tutorial/tutorial-top
tutorial/all-the-things
api/api-top api/api-top
features features
benchmarks benchmarks

View File

@ -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<double>("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<int()> 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<sol::object>( "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<std::function<int()>(); // true
// will not contain data
sol::optional<int> 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<int()> 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<int, int, int> 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<int> will_not_error = lua["abc"]["DOESNOTEXIST"]["ghi"]; // sol::nullopt
int will_not_error2 = lua["abc"]["def"]["ghi"]["jklm"].get_or<int>(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<T> 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<Doge>();
lua["dog"] = std::make_shared<Doge>();
// Identical to above
lua.set("dog", dog);
lua.set("dog", std::move(dog));
lua.set("dog", Doge{});
lua.set("dog", std::unique_ptr<Doge>(new Doge()));
lua.set("dog", std::shared_ptr<Doge>(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>( "player",
sol::constructors<sol::types<>, sol::types<int>, sol::types<int, int>>(), // 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.

View File

@ -18,6 +18,6 @@ If you're already using lua and you just want to use ``sol`` in some places, you
// start using it... // 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! 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!

View File

@ -1,4 +1,4 @@
functions and You functions and You
================= =================
Sol can register all kinds of functions. [ WIP - Check back soon! ] Sol can register all kinds of functions. [ WIP - Check back soon. Until its done, use the :doc:`quick 'n' dirty<all-the-things>` and the :doc:`api<../api/api-top>` to get going! ]

View File

@ -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<all-the-things>` approach and your browser's / editors search function.
.. toctree:: .. toctree::
@ -9,6 +9,7 @@ Ride into the speed lane by learning the all the ins-and-outs of Sol with these
:name: tutorialtoc :name: tutorialtoc
:maxdepth: 1 :maxdepth: 1
all-the-things
getting-started getting-started
existing existing
variables variables

View File

@ -50,12 +50,15 @@ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>
typedef meta::tuple_types<typename traits::return_type> return_types; typedef meta::tuple_types<typename traits::return_type> return_types;
typedef typename traits::args_type args_type; typedef typename traits::args_type args_type;
typedef typename args_type::indices args_indices; typedef typename args_type::indices args_indices;
typedef meta::index_in<this_state, args_type> state_index;
typedef meta::index_in<variadic_args, args_type> va_pack_index;
static const std::size_t arity = traits::arity - static_cast<std::size_t>(state_index::value != SIZE_MAX) - static_cast<std::size_t>(va_pack_index::value != SIZE_MAX);
// compile-time eliminate any functions that we know ahead of time are of improper arity // compile-time eliminate any functions that we know ahead of time are of improper arity
if (meta::find_in_pack_v<index_value<traits::arity>, index_value<M>...>::value) { if (meta::find_in_pack_v<index_value<arity>, index_value<M>...>::value) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
} }
if (traits::arity != fxarity) { if (arity != fxarity) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<arity, M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
} }
if (!stack::stack_detail::check_types<true>().check(args_type(), args_indices(), L, start, no_panic)) { if (!stack::stack_detail::check_types<true>().check(args_type(), args_indices(), L, start, no_panic)) {
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...); return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);

View File

@ -135,7 +135,9 @@ public:
basic_protected_function& operator=(const basic_protected_function&) = default; basic_protected_function& operator=(const basic_protected_function&) = default;
basic_protected_function(basic_protected_function&& ) = default; basic_protected_function(basic_protected_function&& ) = default;
basic_protected_function& operator=(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<base_t>& b) : base_t(b) {}
basic_protected_function(basic_function<base_t>&& 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 #ifdef SOL_CHECK_ARGUMENTS
stack::check<basic_protected_function>(L, index, type_panic); stack::check<basic_protected_function>(L, index, type_panic);
#endif // Safety #endif // Safety

View File

@ -32,6 +32,7 @@ private:
int index; int index;
public: public:
stack_proxy() : L(nullptr), index(0){}
stack_proxy(lua_State* L, int index) : L(L), index(index) {} stack_proxy(lua_State* L, int index) : L(L), index(index) {}
template<typename T> template<typename T>
@ -46,6 +47,16 @@ public:
lua_State* lua_state() const { return L; } lua_State* lua_state() const { return L; }
int stack_index() const { return index; } int stack_index() const { return index; }
template<typename... Ret, typename... Args>
decltype(auto) call(Args&&... args) {
return get<function>().template call<Ret...>(std::forward<Args>(args)...);
}
template<typename... Args>
decltype(auto) operator()(Args&&... args) {
return call<>(std::forward<Args>(args)...);
}
}; };
namespace stack { namespace stack {

View File

@ -24,6 +24,7 @@
#include "error.hpp" #include "error.hpp"
#include "table.hpp" #include "table.hpp"
#include "stack_proxy.hpp"
#include <memory> #include <memory>
namespace sol { 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 { iterator begin () const {
return global.begin(); return global.begin();
} }

View File

@ -143,6 +143,12 @@ namespace meta_detail {
template<typename T, typename... Args> template<typename T, typename... Args>
struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { }; struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { };
template<typename T, typename List>
struct index_in : meta_detail::index_in_pack<0, T, List> { };
template<typename T, typename... Args>
struct index_in<T, types<Args...>> : meta_detail::index_in_pack<0, T, Args...> { };
template<std::size_t I, typename... Args> template<std::size_t I, typename... Args>
struct at_in_pack {}; struct at_in_pack {};

View File

@ -24,8 +24,109 @@
#include "stack.hpp" #include "stack.hpp"
#include "stack_proxy.hpp" #include "stack_proxy.hpp"
#include <iterator>
namespace sol { namespace sol {
struct va_iterator : std::iterator<std::random_access_iterator_tag, stack_proxy, std::ptrdiff_t, stack_proxy*, stack_proxy> {
lua_State* L;
int index;
int stacktop;
stack_proxy sp;
va_iterator() : L(nullptr), index(std::numeric_limits<int>::max()), stacktop(std::numeric_limits<int>::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<int>(idx);
return *this;
}
va_iterator& operator-= (difference_type idx) {
index -= static_cast<int>(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<int>(idx));
}
bool operator==(const va_iterator& r) const {
if (stacktop == std::numeric_limits<int>::max()) {
return r.index == r.stacktop;
}
else if (r.stacktop == std::numeric_limits<int>::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 { struct variadic_args {
private: private:
lua_State* L; lua_State* L;
@ -33,6 +134,16 @@ private:
int stacktop; int stacktop;
public: 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<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
variadic_args() = default; variadic_args() = default;
variadic_args(lua_State* L, int index = -1): L(L), index(lua_absindex(L, index)), stacktop(lua_gettop(L)) {} 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; variadic_args(const variadic_args&) = default;
@ -58,6 +169,20 @@ public:
return *this; 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 push () const {
int pushcount = 0; int pushcount = 0;
for (int i = index; i <= stacktop; ++i) { for (int i = index; i <= stacktop; ++i) {
@ -68,12 +193,12 @@ public:
} }
template<typename T> template<typename T>
decltype(auto) get(int start = 0) const { decltype(auto) get(difference_type start = 0) const {
return stack::get<T>(L, index + start); return stack::get<T>(L, index + static_cast<int>(start));
} }
stack_proxy operator[](int start) const { stack_proxy operator[](difference_type start) const {
return stack_proxy(L, index + start); return stack_proxy(L, index + static_cast<int>(start));
} }
lua_State* lua_state() const { return L; }; lua_State* lua_state() const { return L; };

View File

@ -760,9 +760,9 @@ TEST_CASE("functions/variadic_args", "Check to see we can receive multiple argum
lua.open_libraries(sol::lib::base); lua.open_libraries(sol::lib::base);
lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure { lua.set_function("v", [](sol::this_state, sol::variadic_args va) -> structure {
int r = 0; int r = 0;
for (int i = 0; i < va.leftover_count(); ++i) { for (auto v : va) {
int v = va[i]; int value = v;
r += v; r += value;
} }
return{ r, r > 200 }; return{ r, r > 200 };
}); });

View File

@ -191,7 +191,8 @@ TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down
sol::state lua; sol::state lua;
int begintop = 0, endtop = 0; 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); test_stack_guard g(lua.lua_state(), begintop, endtop);
int traversex24 = lua.traverse_get<int>("t1", "t2", "t3"); int traversex24 = lua.traverse_get<int>("t1", "t2", "t3");