Full documentation overhaul. Add a billion more examples. Fix r-values getting stuck as r-value references in proxy keys.

This commit is contained in:
ThePhD 2018-03-16 16:47:09 -04:00
parent 6c40c559e3
commit 947945d098
24 changed files with 464 additions and 472 deletions

View File

@ -24,13 +24,13 @@ Calls the constructor and creates this type, straight from the stack. For exampl
.. literalinclude:: ../../../examples/tie.cpp
:caption: funcs.lua
:lines: 7-11
:lines: 9-13
:linenos:
The following C++ code will call this function from this file and retrieve the return value:
.. literalinclude:: ../../../examples/tie.cpp
:lines: 16-22
:lines: 1-7,16-22
:linenos:
The call ``woof(20)`` generates a :ref:`unsafe_function_result<unsafe-function-result>`, which is then implicitly converted to an ``double`` after being called. The intermediate temporary ``function_result`` is then destructed, popping the Lua function call results off the Lua stack.

View File

@ -11,101 +11,33 @@ Inspired by a request from `starwing`_ in the :doc:`old sol repository<../origin
When called without the return types being specified by either a ``sol::types<...>`` list or a ``call<Ret...>( ... )`` template type list, it generates a :doc:`protected_function_result<proxy>` class that gets implicitly converted to the requested return type. For example:
.. code-block:: lua
:caption: pfunc_barks.lua
.. literalinclude:: ../../../examples/error_handler.cpp
:linenos:
bark_power = 11;
function got_problems( error_msg )
return "got_problems handler: " .. error_msg
end
function woof ( bark_energy )
if bark_energy < 20
error("*whine*")
end
return (bark_energy * (bark_power / 4))
end
function woofers ( bark_energy )
if bark_energy < 10
error("*whine*")
end
return (bark_energy * (bark_power / 4))
end
:lines: 10-28
The following C++ code will call this function from this file and retrieve the return value, unless an error occurs, in which case you can bind an error handling function like so:
.. code-block:: cpp
.. literalinclude:: ../../../examples/error_handler.cpp
:linenos:
:lines: 1-6,30-66
sol::state lua;
lua.script_file( "pfunc_barks.lua" );
sol::protected_function problematicwoof = lua["woof"];
problematicwoof.error_handler = lua["got_problems"];
auto firstwoof = problematic_woof(20);
if ( firstwoof.valid() ) {
// Can work with contents
double numwoof = first_woof;
}
else{
// An error has occured
sol::error err = first_woof;
}
// errors, calls handler and then returns a string error from Lua at the top of the stack
auto secondwoof = problematic_woof(19);
if (secondwoof.valid()) {
// Call succeeded
double numwoof = secondwoof;
}
else {
// Call failed
// Note that if the handler was successfully called, this will include
// the additional appended error message information of
// "got_problems handler: " ...
sol::error err = secondwoof;
std::string what = err.what();
}
This code is much more long-winded than its :doc:`function<function>` counterpart but allows a person to check for errors. The type here for ``auto`` are ``sol::protected_function_result``. They are implicitly convertible to result types, like all :doc:`proxy-style<proxy>` types are.
Alternatively, with a bad or good function call, you can use ``sol::optional`` to check if the call succeeded or failed:
.. code-block:: cpp
.. literalinclude:: ../../../examples/error_handler.cpp
:linenos:
:lines: 67-
sol::state lua;
lua.script_file( "pfunc_barks.lua" );
sol::protected_function problematicwoof = lua["woof"];
problematicwoof.error_handler = lua["got_problems"];
sol::optional<double> maybevalue = problematicwoof(19);
if (maybevalue) {
// Have a value, use it
double numwoof = maybevalue.value();
}
else {
// No value!
}
That makes the code a bit more concise and easy to reason about if you don't want to bother with reading the error. Thankfully, unlike ``sol::unsafe_function_result``, you can save ``sol::protected_function_result`` in a variable and push/pop things above it on the stack where its returned values are. This makes it a bit more flexible than the rigid, performant ``sol::unsafe_function_result`` type that comes from calling :doc:`sol::unsafe_function<function>`.
If you're confident the result succeeded, you can also just put the type you want (like ``double`` or ``std::string`` right there and it will get it. But, if it doesn't work out, sol can throw and/or panic if you have the :doc:`safety<../safety>` features turned on:
If you're confident the result succeeded, you can also just put the type you want (like ``double`` or ``std::string``) right there and it will get it. But, if it doesn't work out, sol can throw and/or panic if you have the :doc:`safety<../safety>` features turned on:
.. code-block:: cpp
:linenos:
sol::state lua;
lua.script_file( "pfunc_barks.lua" );
// construct with function + error handler
// shorter than old syntax
sol::protected_function problematicwoof(lua["woof"], lua["got_problems"]);
@ -119,9 +51,6 @@ Finally, it is *important* to note you can set a default handler. The function i
.. code-block:: cpp
:linenos:
sol::state lua;
lua.script_file( "pfunc_barks.lua" );
// sets got_problems as the default
// handler for all protected_function errors
sol::protected_function::set_default_handler(lua["got_problems"]);

View File

@ -25,55 +25,23 @@ These classes provide implicit assignment operator ``operator=`` (for ``set``) a
proxy
-----
``proxy`` is returned by lookups into :doc:`sol::table<table>` and table-like entities. Because it is templated on key and table type, it would be hard to spell: you can capture it using the word ``auto`` if you feel like you need to carry it around for some reason before using it. ``proxy`` evaluates its arguments lazily, when you finally call ``get`` or ``set`` on it. Here are some examples given the following lua script.
``proxy`` is returned by lookups into :doc:`sol::table<table>` and table-like entities. Because it is templated on key and table type, it would be hard to spell: you can capture it using the word ``auto`` if you feel like you need to carry it around for some reason before using it. ``proxy`` evaluates its arguments lazily, when you finally call ``get`` or ``set`` on it. Here are some examples given the following lua script:
.. code-block:: lua
.. literalinclude:: ../../../examples/table_proxy.cpp
:linenos:
:caption: lua nested table script
bark = {
woof = {
[2] = "arf!"
}
}
:lines: 11-15
After loading that file in or putting it in a string and reading the string directly in lua (see :doc:`state`), you can start kicking around with it in C++ like so:
.. code-block:: c++
.. literalinclude:: ../../../examples/table_proxy.cpp
:linenos:
:lines: 1-8,18-40
sol::state lua;
We don't recommend using ``proxy`` lazy evaluation the above to be used across classes or between function: it's more of something you can do to save a reference to a value you like, call a script or run a lua function, and then get it afterwards. You can also set functions (and function objects) this way, and retrieve them as well:
// produces proxy, implicitly converts to std::string, quietly destroys proxy
std::string x = lua["bark"]["woof"][2];
``proxy`` lazy evaluation:
.. code-block:: c++
.. literalinclude:: ../../../examples/table_proxy.cpp
:linenos:
:caption: multi-get
auto x = lua["bark"];
auto y = x["woof"];
auto z = x[2];
// retrivies value inside of lua table above
std::string value = z; // "arf!"
// Can change the value later...
z = 20;
// Yay, lazy-evaluation!
int changed_value = z; // now it's 20!
We don't recommend the above to be used across classes or between function: it's more of something you can do to save a reference to a value you like, call a script or run a lua function, and then get it afterwards. You can also set functions (and function objects) this way, and retrieve them as well.
.. code-block:: c++
:linenos:
lua["bark_value"] = 24;
lua["chase_tail"] = floof::chase_tail; // chase_tail is a free function
:lines: 41-
members
-------
@ -192,30 +160,5 @@ on function objects and proxies
.. note::
As of recent versions of sol2 (2.18.2 and above), this is no longer an issue, as even bound classes will have any detectable function call operator automatically bound to the object, to allow this to work without having to use ``.set`` or ``.set_function``. The note here is kept for posterity and information for older versions.
As of recent versions of sol2 (2.18.2 and above), this is no longer an issue, as even bound classes will have any detectable function call operator automatically bound to the object, to allow this to work without having to use ``.set`` or ``.set_function``. The note here is kept for posterity and information for older versions. There are only some small caveats, see: :ref:`this note here<binding-callable-objects>`.
.. warning::
*The below information is outdated.*
Consider the following:
.. code-block:: cpp
:linenos:
:caption: Note 1 Case
struct doge {
int bark;
void operator()() {
bark += 1;
}
};
sol::state lua;
lua["object"] = doge{}; // bind constructed doge to "object"
// but it binds as a function
When you use the ``lua["object"] = doge{};`` from above, keep in mind that Sol detects if this is a function *callable with any kind of arguments*. Since ``doge`` has overriden ``return_type operator()( argument_types... )`` on itself, it results in satisfying the ``requires`` constraint from above. This means that if you have a user-defined type you want to bind as a :doc:`userdata with usertype semantics<usertype>` with this syntax, it might get bound as a function and not as a user-defined type (d'oh!). use ``lua["object"].set(doge)`` directly to avoid this, or ``lua["object"].set_function(doge{})`` to perform this explicitly.

View File

@ -298,7 +298,7 @@ This is an SFINAE-friendly struct that is meant to expose a function ``check=``
.. note::
You must turn it on with ``SOL_ENABLE_INTEROP``, as described in the :ref:`config and safety section<config>`.
You must turn this feature on with ``SOL_ENABLE_INTEROP``, as described in the :ref:`config and safety section<config>`.
.. code-block:: cpp

View File

@ -100,29 +100,10 @@ If your script returns a value, you can capture it from the returned :ref:`sol::
To handle errors when using the second overload, provide a callable function/object that takes a ``lua_State*`` as its first argument and a ``sol::protected_function_result`` as its second argument. ``sol::script_default_on_error`` and ``sol::script_pass_on_error`` are 2 functions provided by sol that will either generate a traceback error to return / throw (if throwing is allowed); or, pass the error on through and return it to the user (respectively). An example of having your:
.. code-block:: cpp
:caption: running code safely
.. literalinclude:: ../../../examples/docs/state_script_safe.cpp
:linenos:
:name: state-script-safe
int main () {
sol::state lua;
// uses sol::script_default_on_error, which either panics or throws,
// depending on your configuration and compiler settings
auto result1 = lua.safe_script("bad.code");
// a custom handler that you write yourself
// is only called when an error happens with loading or running the script
auto result2 = lua.safe_script("123 bad.code", [](lua_State* L, sol::protected_function_result pfr) {
// pfr will contain things that went wrong, for either loading or executing the script
// the user can do whatever they like here, including throw. Otherwise...
sol::error err = pfr;
std::cout << err.what() << std::endl;
// ... they need to return the protected_function_result
return pfr;
});
}
You can also pass a :doc:`sol::environment<environment>` to ``script``/``script_file`` to have the script have sandboxed / contained in a way inside of a state. This is useful for runnig multiple different "perspectives" or "views" on the same state, and even has fallback support. See the :doc:`sol::environment<environment>` documentation for more details.
.. code-block:: cpp

View File

@ -9,23 +9,5 @@ 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
.. literalinclude:: ../../../examples/this_state.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

@ -1,6 +1,6 @@
tie
===
*improved version of ``std::tie``*
*improved version of std::tie*
`std::tie()`_ does not work well with :doc:`sol::function<function>`'s ``sol::function_result`` returns. Use ``sol::tie`` instead. Because they're both named `tie`, you'll need to be explicit when you use Sol's by naming it with the namespace (``sol::tie``), even with a ``using namespace sol;``. Here's an example:

View File

@ -3,92 +3,11 @@ usertype<T>
*structures and classes from C++ made available to Lua code*
*Note: ``T`` refers to the type being turned into a usertype.*
While other frameworks extend lua's syntax or create Data Structure Languages (DSLs) to create classes in Lua, :doc:`Sol<../index>` instead offers the ability to generate easy bindings. These use metatables and userdata in Lua for their implementation. Usertypes are also `runtime extensible`_. If you need a usertype that has less compiler crunch-time to it, try the :doc:`simple version of this after reading these docs<simple_usertype>` Given this C++ class:
.. code-block:: cpp
:linenos:
.. note::
struct ship {
int bullets = 20;
int life = 100;
bool shoot () {
if (bullets > 0) {
--bullets;
// successfully shot
return true;
}
// cannot shoot
return false;
}
bool hurt (int by) {
life -= by;
// have we died?
return life < 1;
}
};
You can bind the it to Lua using the following C++ code:
.. code-block:: cpp
:linenos:
sol::state lua;
lua.new_usertype<ship>( "ship", // the name of the class, as you want it to be used in lua
// List the member functions you wish to bind:
// "name_of_item", &class_name::function_or_variable
"shoot", &ship::shoot,
"hurt", &ship::hurt,
// bind variable types, too
"life", &ship::life,
// names in lua don't have to be the same as C++,
// but it probably helps if they're kept the same,
// here we change it just to show its possible
"bullet_count", &ship::bullets
);
Equivalently, you can also write:
.. code-block:: cpp
:linenos:
:emphasize-lines: 4,12
sol::state lua;
// Use constructor directly
usertype<ship> shiptype(
"shoot", &ship::shoot,
"hurt", &ship::hurt,
"life", &ship::life,
"bullet_count", &ship::bullets
);
// set usertype explicitly, with the given name
lua.set_usertype( "ship", shiptype );
// the shiptype variable is now a useless skeleton type, just let it destruct naturally and don't use it again.
Note that here, because the C++ class is default-constructible, it will automatically generate a creation function that can be called in lua called "new" that takes no arguments. You can use it like this in lua code:
.. code-block:: lua
:linenos:
fwoosh = ship.new()
-- note the ":" that is there: this is mandatory for member function calls
-- ":" means "pass self" in Lua
local success = fwoosh:shoot()
local is_dead = fwoosh:hurt(20)
-- check if it works
print(is_dead) -- the ship is not dead at this point
print(fwoosh.life .. "life left") -- 80 life left
print(fwoosh.bullet_count) -- 19
``T`` refers to the type being turned into a usertype.
While other frameworks extend lua's syntax or create Data Structure Languages (DSLs) to create classes in Lua, :doc:`Sol<../index>` instead offers the ability to generate easy bindings that pile on performance. You can see a `small starter example here`_. These use metatables and userdata in Lua for their implementation. Usertypes are also `runtime extensible`_.
There are more advanced use cases for how to create and use a usertype, which are all based on how to use its constructor (see below).
@ -329,7 +248,7 @@ Register the base classes explicitly.
:caption: inheritance.cpp
:name: inheritance-example
:linenos:
:emphasize-lines: 5
:emphasize-lines: 23
.. note::
@ -384,5 +303,6 @@ performance note
.. _destructible: http://en.cppreference.com/w/cpp/types/is_destructible
.. _default_constructible: http://en.cppreference.com/w/cpp/types/is_constructible
.. _small starter example here: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_basics.cpp
.. _runtime extensible: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_advanced.cpp#L81
.. _the metamethods in the Lua manual: https://www.lua.org/manual/5.3/manual.html#2.4

View File

@ -12,12 +12,12 @@ In general, we always insert a ``T*`` in the first ``sizeof(T*)`` bytes, so the
.. warning::
The layout of memory described below does **not** take into account alignment. sol2 now takes alignment into account and aligns memory, which is important for misbehaving allocators and types that do not align well to the size of a pointer on their system. If you need to obtain proper alignments for usertypes stored in userdata pointers, **please** use the detail functions named ``sol::detail::align_usertype_pointer``, ``sol::detail::align_usertype``, and ``sol::detail::align_usertype_unique``. This will shift a ``void*`` pointer by the appropriate amount to reach a certain section in memory. For almost all use cases, please use ``void* memory = lua_touserdata(L, index);``, followed by ``memory = sol::detail::align_usertype_pointer( memory );`` to adjust the pointer to be at the right place.
The layout of memory described below does **not** take into account alignment. sol2 now takes alignment into account and aligns memory, which is important for misbehaving allocators and types that do not align well to the size of a pointer on their system. If you need to obtain proper alignments for usertypes stored in userdata pointers, **please** use the detail functions named ``sol::detail::align_usertype_pointer``, ``sol::detail::align_usertype``, and ``sol::detail::align_usertype_unique``. This will shift a ``void*`` pointer by the appropriate amount to reach a certain section in memory. For almost all other use cases, please use ``void* memory = lua_touserdata(L, index);``, followed by ``memory = sol::detail::align_usertype_pointer( memory );`` to adjust the pointer to be at the right place.
.. warning::
The code from below is only guaranteed to work 100% of the time if you define :ref:`SOL_NO_MEMORY_ALIGNMENT<config-memory>`.
The diagrams and explanations from below is only guaranteed to work 100% of the time if you define :ref:`SOL_NO_MEMORY_ALIGNMENT<config-memory>`. Be aware that this may result in unaligned reads/writes, which can crash some older processors and trigger static analyzer/instrumentation tool warnings, like Clang's Address Sanitizer (ASan).
To retrieve a ``T``

View File

@ -5,45 +5,5 @@ var
The sole purpose of this tagging type is to work with :doc:`usertypes<usertype>` to provide ``my_class.my_static_var`` access, and to also provide reference-based access as well.
.. code-block:: cpp
#include <sol.hpp>
struct test {
static int muh_variable;
};
int test::muh_variable = 25;
int main () {
sol::state lua;
lua.open_libraries();
lua.new_usertype<test>("test",
"direct", sol::var(2),
"global", sol::var(test::muh_variable),
"ref_global", sol::var(std::ref(test::muh_variable))
);
int direct_value = lua["test"]["direct"];
// direct_value == 2
int global = lua["test"]["global"];
// global == 25
int global2 = lua["test"]["ref_global"];
// global2 == 25
test::muh_variable = 542;
global = lua["test"]["global"];
// global == 25
// global is its own memory: was passed by value
global2 = lua["test"]["ref_global"];
// global2 == 542
// global2 was passed through std::ref
// global2 holds a reference to muh_variable
// if muh_variable goes out of scope or is deleted
// problems could arise, so be careful!
return 0;
}
.. literalinclude:: ../../../examples/usertype_var.cpp
:linenos:

View File

@ -11,116 +11,15 @@ This class is meant to represent every single argument at its current index and
``variadic_args`` also has ``begin()`` and ``end()`` functions that return (almost) random-acess iterators. These return a proxy type that can be implicitly converted to a type you want, much like the :doc:`table proxy type<proxy>`.
.. code-block:: cpp
.. literalinclude:: ../../../examples/variadic_args.cpp
:linenos:
#include <sol.hpp>
You can also "save" arguments and the like later, by stuffing them into a ``std::vector<sol::object>`` or something similar that serializes them into the registry. Below is an example of saving all of the arguments provided by ``sol::variadic_args`` in a lambda capture variable called ``args``.
int main () {
sol::state lua;
lua.open_libraries(sol::lib::base);
// 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", [](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
return 0;
}
You can also "save" arguments and the like later, by stuffing them into a ``std::vector<sol::object>`` or something similar that pulls out all the arguments. Below is an example of saving all of the arguments provided by ``sol::variadic_args`` in a lambda capture variable called ``args``.
.. code-block:: cpp
.. literalinclude:: ../../../examples/variadic_args_storage.cpp
:linenos:
#include "sol.hpp"
#include <functional>
Finally, note that you can use ``sol::variadic_args`` constructor to "offset"/"shift over" the arguments being viewed:
std::function<void()> function_storage;
void store_routine(const sol::function& f, const sol::variadic_args& va) {
function_storage = [=, args = std::vector<sol::object>(va.begin(), va.end())]() {
f(sol::as_args(args));
};
}
int main() {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("store_routine", &store_routine);
lua.script(R"(
function a(name)
print(name)
end
store_routine(a, "some name")
)");
function_storage();
lua.script(R"(
function b(number, text)
print(number, "of", text)
end
store_routine(b, 20, "these apples")
)");
function_storage();
return 0;
}
Finally, note that you can use ``sol::variadic_args`` constructor to "offset" which arguments you want:
.. code-block:: cpp
.. literalinclude:: ../../../examples/variadic_args_shifted.cpp
:linenos:
#include <sol.hpp>
int main () {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](sol::variadic_args va) {
int r = 0;
sol::variadic_args shifted_va(va.lua_state(), 3);
for (auto v : shifted_va) {
int value = v;
r += value;
}
return r;
});
lua.script("x = f(1, 2, 3, 4)");
lua.script("x2 = f(8, 200, 3, 4)");
lua.script("x3 = f(1, 2, 3, 4, 5, 6)");
lua.script("print(x)"); // 7
lua.script("print(x2)"); // 7
lua.script("print(x3)"); // 18
return 0;
}

View File

@ -1,6 +1,13 @@
CMake Script
============
sol2 comes with a CMake script in the top level. It is primarily made for building and running the examples and tests, but it includes exported and configured targets (``sol2``, ``sol2_single``) for your use. If you have any problems with it or its targets, please do file a report, or a pull request because CMake is not my forte.
.. warning::
The below is slightly outdated, but will probably still work for you!
Thanks to `Kevin Brightwell`_, you can drop this CMake Script into your CMake Projects to have Sol part of one of its builds:
.. code-block:: cmake

View File

@ -1,17 +1,19 @@
origin
======
In the beginning, there was Sir Dennis Ritchie. And Ritchie saw the void, and outstretched his hand, and commanded "Let there be water." And lo, it was so, and there was the C. And with the C, other entities dared to venture on the void given form. Lord Bjarne Stroustrup too did outstretch his hands and say "Let there be an abundance." And lo, into the sea was cast a double portion of surplus of all the things that swam. And for a while, it was good. But other entities were still curious about what yet lay undefined, and one such pantheon, PUC-RIO, saw that it fitting to create the moon. And with the waters and sea made and the moon cast in a starry night sky, PUC-RIO and Dennis and Stroustrup saw that they did good. They oversaw the moon and the sea and gave sound council and it grew. But as the time grew, humanity grew... discontent. No longer were the simple fishing rods and the flowing tides and the dark sky enough lit by a pale moon, no matter how miraculously they were made, enough. They sought out more.
In the beginning, there was Sir Dennis Ritchie. And Ritchie saw the void, and outstretched his hand, and commanded "Let there be water." And lo, it was so, and there was the C. And with the C, other entities dared to venture on the void given form. Lord Bjarne Stroustrup too did outstretch his hands and say "Let there be an abundance." And lo, into the sea was cast a double portion of surplus of all the things that swam. And for a while, it was good. But other entities were still curious about what yet lay undefined, and one such pantheon, PUC-RIO, saw that it fitting to create. And thusly, they banded together and declared "Let there be a Moon". And thusly, the moon was born into the sky.
And with the waters and sea made and the moon cast in a starry night sky, PUC-RIO and Ritchie and Stroustrup saw that they did good. They oversaw the moon and the sea and all its abundance, and gave sound council and it overflowed wonderfully. But as the time grew, life grew... discontent. No longer were the simple fishing rods and the flowing tides and the dark sky enough lit by a pale moon and stars enough, no matter how miraculously they were made. They sought out more.
They sought out the light.
And lo, `Danny Y., Rapptz`_ did stand firm in the sea and cast his hands to the sky and said "Let there be Light!". And in the sky was cast a sun. It was an early sun, a growing sun, and many gathered to its warmth, marveling at a life they never knew. And he saw that it was good...
And lo, `Danny Y., Rapptz`_ did stand firm in the sea and cast his hands to the sky and said "Let there be Light!". And in the sky was cast a Sun. It was an early sun, a growing sun, and many gathered to its warmth, marveling at a life they never knew. And he saw that it was good...
seriously
---------
Sol was originally started by many moon cycles ago to interop with Lua and C++. `Rapptz`_ It was very successful and many rejoiced at having an easy to use abstraction on top of the Lua API. Rapptz continued to make a number of great projects and has been busy with other things, so upon seeing the repository grow stagnant and tired in the last very long while (over a year), `ThePhD`_ forked it into Sol2 and rebooted the code with the hopes of reaching the Milestone and the documentation you have today.
Sol was originally started by many moon cycles ago to interop with Lua and C++, by `Rapptz`_. It was very successful and many rejoiced at having an easy to use abstraction on top of the Lua API. Rapptz continued to make a number of great projects and has been busy with other things, so upon seeing the repository grow stagnant and tired in the last very long while (over a year), `ThePhD`_ forked it into Sol2 and rebooted the code with the hopes of reaching the Milestone and the documentation you have today.
To get to the old repo, head over `here`_.
@ -19,7 +21,7 @@ To get to the old repo, head over `here`_.
the name
--------
Sol means sun. The moon (Lua) needs a sun, because without it only the bleak night of copy-paste programming and off-by-one errors would prevail.
Sol means sun. The moon (Lua) needs a sun, because without it only the bleak night of copy-paste programming and off-by-one errors would prevail. ... Or something.
.. _here: https://github.com/Rapptz/sol

View File

@ -55,7 +55,7 @@ To run Lua code but have an error handler in case things go wrong:
.. literalinclude:: ../../../examples/tutorials/quick_n_dirty/running_lua_code.cpp
:linenos:
:lines: 28-40
:lines: 28-39,47-49
running lua code (low-level)
@ -70,7 +70,7 @@ You can use the individual load and function call operator to load, check, and t
.. literalinclude:: ../../../examples/tutorials/quick_n_dirty/running_lua_code_low_level.cpp
:linenos:
:lines: 1-10, 16-41
:lines: 1-10, 16-40, 47-49
set and get variables
---------------------

View File

@ -0,0 +1,35 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
int main () {
std::cout << "=== safe_script usage ===" << std::endl;
sol::state lua;
// uses sol::script_default_on_error, which either panics or throws,
// depending on your configuration and compiler settings
try {
auto result1 = lua.safe_script("bad.code");
}
catch( const sol::error& e ) {
std::cout << "an expected error has occurred: " << e.what() << std::endl;
}
// a custom handler that you write yourself
// is only called when an error happens with loading or running the script
auto result2 = lua.safe_script("123 bad.code", [](lua_State*, sol::protected_function_result pfr) {
// pfr will contain things that went wrong, for either loading or executing the script
// the user can do whatever they like here, including throw. Otherwise...
sol::error err = pfr;
std::cout << "An error (an expected one) occurred: " << err.what() << std::endl;
// ... they need to return the protected_function_result
return pfr;
});
std::cout << std::endl;
return 0;
}

View File

@ -0,0 +1,82 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
int main () {
const auto& code = R"(
bark_power = 11;
function got_problems( error_msg )
return "got_problems handler: " .. error_msg
end
function woof ( bark_energy )
if bark_energy < 20 then
error("*whine*")
end
return (bark_energy * (bark_power / 4))
end
function woofers ( bark_energy )
if bark_energy < 10 then
error("*whine*")
end
return (bark_energy * (bark_power / 4))
end
)";
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script(code);
sol::protected_function problematic_woof = lua["woof"];
problematic_woof.error_handler = lua["got_problems"];
auto firstwoof = problematic_woof(20);
if ( firstwoof.valid() ) {
// Can work with contents
double numwoof = firstwoof;
std::cout << "Got value: " << numwoof << std::endl;
}
else{
// An error has occured
sol::error err = firstwoof;
std::string what = err.what();
std::cout << what << std::endl;
}
// errors, calls handler and then returns a string error from Lua at the top of the stack
auto secondwoof = problematic_woof(19);
if (secondwoof.valid()) {
// Call succeeded
double numwoof = secondwoof;
std::cout << "Got value: " << numwoof << std::endl;
}
else {
// Call failed
// Note that if the handler was successfully called, this will include
// the additional appended error message information of
// "got_problems handler: " ...
sol::error err = secondwoof;
std::string what = err.what();
std::cout << what << std::endl;
}
// can also use optional to tell things
sol::optional<double> maybevalue = problematic_woof(19);
if (maybevalue) {
// Have a value, use it
double numwoof = maybevalue.value();
std::cout << "Got value: " << numwoof << std::endl;
}
else {
std::cout << "No value!" << std::endl;
}
std::cout << std::endl;
return 0;
}

55
examples/table_proxy.cpp Normal file
View File

@ -0,0 +1,55 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include "assert.hpp"
#include <iostream>
int main () {
const auto& code = R"(
bark = {
woof = {
[2] = "arf!"
}
}
)";
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script(code);
// produces proxy, implicitly converts to std::string, quietly destroys proxy
std::string arf_string = lua["bark"]["woof"][2];
// lazy-evaluation of tables
auto x = lua["bark"];
auto y = x["woof"];
auto z = y[2];
// retrivies value inside of lua table above
std::string value = z;
c_assert(value == "arf!");
// Can change the value later...
z = 20;
// Yay, lazy-evaluation!
int changed_value = z; // now it's 20!
c_assert(changed_value == 20);
lua.script("assert(bark.woof[2] == 20)");
lua["a_new_value"] = 24;
lua["chase_tail"] = [](int chasing) {
int r = 2;
for (int i = 0; i < chasing; ++i) {
r *= r;
}
return r;
};
lua.script("assert(a_new_value == 24)");
lua.script("assert(chase_tail(2) == 16)");
return 0;
}

View File

@ -9,53 +9,57 @@
int main() {
std::cout << "=== tables ===" << std::endl;
sol::state lua;
// table used as an array
lua.script("table1 = {\"hello\", \"table\"}");
// table with a nested table and the key value syntax
lua.script("table2 = {"
"[\"nestedTable\"] = {"
"[\"key1\"] = \"value1\","
"[\"key2\"]= \"value2\""
"},"
"[\"name\"]= \"table2\""
"}");
sol::state lua;
// table used as an array
lua.script(R"(table1 = {"hello", "table"})");
// table with a nested table and the key value syntax
lua.script(R"(
table2 = {
["nestedTable"] = {
["key1"] = "value1",
["key2"]= "value2",
},
["name"] = "table2",
}
)");
/* Shorter Syntax: */
// using the values stored in table1
/*std::cout << (std::string)lua["table1"][1] << " "
<< (std::string)lua["table1"][2] << '\n';
/* Shorter Syntax: */
// using the values stored in table1
/*std::cout << (std::string)lua["table1"][1] << " "
<< (std::string)lua["table1"][2] << '\n';
*/
// some retrieval of values from the nested table
// the cleaner way of doing things
// chain off the the get<>() / [] results
auto t2 = lua.get<sol::table>("table2");
auto nestedTable = t2.get<sol::table>("nestedTable");
// Alternatively:
//sol::table t2 = lua["table2"];
//sol::table nestedTable = t2["nestedTable"];
std::string x = lua["table2"]["nestedTable"]["key2"];
std::cout << "nested table: key1 : " << nestedTable.get<std::string>("key1") << ", key2: "
<< x
<< '\n';
std::cout << "name of t2: " << t2.get<std::string>("name") << '\n';
std::string t2name = t2["name"];
std::cout << "name of t2: " << t2name << '\n';
// some retrieval of values from the nested table
// the cleaner way of doing things
// chain off the the get<>() / [] results
auto t2 = lua.get<sol::table>("table2");
auto nestedTable = t2.get<sol::table>("nestedTable");
// Alternatively:
//sol::table t2 = lua["table2"];
//sol::table nestedTable = t2["nestedTable"];
std::string x = lua["table2"]["nestedTable"]["key2"];
std::cout << "nested table: key1 : " << nestedTable.get<std::string>("key1") << ", key2: "
<< x
<< '\n';
std::cout << "name of t2: " << t2.get<std::string>("name") << '\n';
std::string t2name = t2["name"];
std::cout << "name of t2: " << t2name << '\n';
/* Longer Syntax: */
// using the values stored in table1
std::cout << lua.get<sol::table>("table1").get<std::string>(1) << " "
<< lua.get<sol::table>("table1").get<std::string>(2) << '\n';
/* Longer Syntax: */
// using the values stored in table1
std::cout << lua.get<sol::table>("table1").get<std::string>(1) << " "
<< lua.get<sol::table>("table1").get<std::string>(2) << '\n';
// some retrieval of values from the nested table
// the cleaner way of doing things
std::cout << "nested table: key1 : " << nestedTable.get<std::string>("key1") << ", key2: "
// yes you can chain the get<>() results
<< lua.get<sol::table>("table2").get<sol::table>("nestedTable").get<std::string>("key2")
<< '\n';
std::cout << "name of t2: " << t2.get<std::string>("name") << '\n';
// some retrieval of values from the nested table
// the cleaner way of doing things
std::cout << "nested table: key1 : " << nestedTable.get<std::string>("key1") << ", key2: "
// yes you can chain the get<>() results
<< lua.get<sol::table>("table2").get<sol::table>("nestedTable").get<std::string>("key2")
<< '\n';
std::cout << "name of t2: " << t2.get<std::string>("name") << '\n';
std::cout << std::endl;
return 0;
}

29
examples/this_state.cpp Normal file
View File

@ -0,0 +1,29 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include "assert.hpp"
int main () {
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
int first = lua["first"];
c_assert(first == 6);
int second = lua["second"];
c_assert(second == 6);
return 0;
}

View File

@ -0,0 +1,84 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
struct ship {
int bullets = 20;
int life = 100;
bool shoot () {
if (bullets > 0) {
--bullets;
// successfully shot
return true;
}
// cannot shoot
return false;
}
bool hurt (int by) {
life -= by;
// have we died?
return life < 1;
}
};
int main () {
std::cout << "=== usertype basics ===" << std::endl;
static const bool way_1 = true;
sol::state lua;
lua.open_libraries(sol::lib::base);
if (way_1) {
lua.new_usertype<ship>( "ship", // the name of the class, as you want it to be used in lua
// List the member functions you wish to bind:
// "name_of_item", &class_name::function_or_variable
"shoot", &ship::shoot,
"hurt", &ship::hurt,
// bind variable types, too
"life", &ship::life,
// names in lua don't have to be the same as C++,
// but it probably helps if they're kept the same,
// here we change it just to show its possible
"bullet_count", &ship::bullets
);
}
else {
// an alternative way:
// use the constructor directly
sol::usertype<ship> shiptype(
"shoot", &ship::shoot,
"hurt", &ship::hurt,
"life", &ship::life,
"bullet_count", &ship::bullets
);
// set usertype explicitly, with the given name
lua.set_usertype( "ship", shiptype );
// the shiptype variable is now a useless skeleton type, just let it destruct naturally and don't use it again.
}
const auto& code = R"(
fwoosh = ship.new()
-- note the ":" that is there: this is mandatory for member function calls
-- ":" means "pass self" in Lua
local success = fwoosh:shoot()
local is_dead = fwoosh:hurt(20)
-- check if it works
print(is_dead) -- the ship is not dead at this point
print(fwoosh.life .. "life left") -- 80 life left
print(fwoosh.bullet_count) -- 19
)";
lua.script(code);
std::cout << std::endl;
return 0;
}

View File

@ -38,5 +38,8 @@ int main() {
lua.script("print(x)"); // 50
lua.script("print(x2)"); // 600
lua.script("print(x3)"); // 21
std::cout << std::endl;
return 0;
}

View File

@ -0,0 +1,34 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
int main () {
std::cout << "=== variadic_args shifting constructor ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](sol::variadic_args va) {
int r = 0;
sol::variadic_args shifted_va(va.lua_state(), 3);
for (auto v : shifted_va) {
int value = v;
r += value;
}
return r;
});
lua.script("x = f(1, 2, 3, 4)");
lua.script("x2 = f(8, 200, 3, 4)");
lua.script("x3 = f(1, 2, 3, 4, 5, 6)");
lua.script("print(x)"); // 7
lua.script("print(x2)"); // 7
lua.script("print(x3)"); // 18
std::cout << std::endl;
return 0;
}

View File

@ -0,0 +1,43 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
#include <functional>
int main() {
std::cout << "=== variadic_args serialization/storage ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
std::function<void()> function_storage;
auto store_routine = [&function_storage] (sol::function f, sol::variadic_args va) {
function_storage = [f, args = std::vector<sol::object>(va.begin(), va.end())]() {
f(sol::as_args(args));
};
};
lua.set_function("store_routine", store_routine);
lua.script(R"(
function a(name)
print(name)
end
store_routine(a, "some name")
)");
function_storage();
lua.script(R"(
function b(number, text)
print(number, "of", text)
end
store_routine(b, 20, "these apples")
)");
function_storage();
std::cout << std::endl;
return 0;
}

View File

@ -602,7 +602,7 @@ namespace sol {
namespace meta_detail {
template <typename T, meta::disable<meta::is_specialization_of<meta::unqualified_t<T>, std::tuple>> = meta::enabler>
decltype(auto) force_tuple(T&& x) {
return std::forward_as_tuple(std::forward<T>(x));
return std::tuple<std::decay_t<T>>(std::forward<T>(x));
}
template <typename T, meta::enable<meta::is_specialization_of<meta::unqualified_t<T>, std::tuple>> = meta::enabler>