mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
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:
parent
69ad2b4c71
commit
006357430b
|
@ -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
|
||||
|
|
|
@ -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>`.
|
||||
|
||||
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.
|
58
docs/source/api/property.rst
Normal file
58
docs/source/api/property.rst
Normal 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)
|
|
@ -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<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.
|
||||
|
||||
.. 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<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
|
||||
: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>
|
||||
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>
|
||||
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>`.
|
||||
|
||||
|
|
31
docs/source/api/this_state.rst
Normal file
31
docs/source/api/this_state.rst
Normal 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
|
||||
|
|
@ -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, ...>``
|
||||
- ``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"``
|
||||
* ``"{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.
|
||||
- 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<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>`.
|
||||
|
||||
|
@ -198,7 +204,7 @@ You must specify the ``sol::base_classes`` tag with the ``sol::bases<Types...>()
|
|||
};
|
||||
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<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
|
||||
------
|
||||
|
||||
|
|
42
docs/source/api/variadic_args.rst
Normal file
42
docs/source/api/variadic_args.rst
Normal 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
|
|
@ -30,6 +30,7 @@ what Sol supports
|
|||
* User-Defined Type (:doc:`sol::usertype<api/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<benchmarks>`)
|
||||
* Lots of users, but the Repository is kinda stagnant...
|
||||
|
||||
|
||||
luawrapper -
|
||||
|
||||
* Takes the approach of writing and reading tables using ``readVariable`` and ``writeVariable`` functions
|
||||
|
|
|
@ -22,6 +22,7 @@ get going:
|
|||
:name: mastertoc
|
||||
|
||||
tutorial/tutorial-top
|
||||
tutorial/all-the-things
|
||||
api/api-top
|
||||
features
|
||||
benchmarks
|
||||
|
|
441
docs/source/tutorial/all-the-things.rst
Normal file
441
docs/source/tutorial/all-the-things.rst
Normal 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.
|
|
@ -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!
|
|
@ -1,4 +1,4 @@
|
|||
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! ]
|
|
@ -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::
|
||||
|
@ -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
|
||||
|
|
|
@ -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 typename traits::args_type args_type;
|
||||
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
|
||||
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)...);
|
||||
}
|
||||
if (traits::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)...);
|
||||
if (arity != fxarity) {
|
||||
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)) {
|
||||
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)...);
|
||||
|
|
|
@ -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<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
|
||||
stack::check<basic_protected_function>(L, index, type_panic);
|
||||
#endif // Safety
|
||||
|
|
|
@ -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<typename T>
|
||||
|
@ -46,6 +47,16 @@ public:
|
|||
|
||||
lua_State* lua_state() const { return L; }
|
||||
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 {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "error.hpp"
|
||||
#include "table.hpp"
|
||||
#include "stack_proxy.hpp"
|
||||
#include <memory>
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -143,6 +143,12 @@ namespace meta_detail {
|
|||
template<typename T, typename... 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>
|
||||
struct at_in_pack {};
|
||||
|
||||
|
|
|
@ -24,8 +24,109 @@
|
|||
|
||||
#include "stack.hpp"
|
||||
#include "stack_proxy.hpp"
|
||||
#include <iterator>
|
||||
|
||||
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 {
|
||||
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<iterator> reverse_iterator;
|
||||
typedef std::reverse_iterator<const_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<typename T>
|
||||
decltype(auto) get(int start = 0) const {
|
||||
return stack::get<T>(L, index + start);
|
||||
decltype(auto) get(difference_type start = 0) const {
|
||||
return stack::get<T>(L, index + static_cast<int>(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<int>(start));
|
||||
}
|
||||
|
||||
lua_State* lua_state() const { return L; };
|
||||
|
|
|
@ -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 };
|
||||
});
|
||||
|
|
|
@ -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<int>("t1", "t2", "t3");
|
||||
|
|
Loading…
Reference in New Issue
Block a user