diff --git a/docs/source/functions.rst b/docs/source/functions.rst index 0732a000..1986bf0d 100644 --- a/docs/source/functions.rst +++ b/docs/source/functions.rst @@ -31,12 +31,48 @@ working with callables/lambdas To be explicit about wanting a struct to be interpreted as a function, use ``mytable.set_function( key, func_value );``. You can be explicit about wanting a function as well by using the :doc:`sol::as_function<../api/as_function>` call, which will wrap and identify your type as a function. +.. note:: + + As of sol 2.18.1, the below + .. note:: Function objects ``obj`` -- a struct with a ``return_type operator()( ... )`` member defined on them, like all C++ lambdas -- are not interpreted as functions when you use ``set`` for ``mytable.set( key, value )`` and ``state.create_table(_with)( ... )``. This only happens automagically with ``mytable[key] = obj``. Note that this also applies to calling functions, for example: ``my_state["table"]["sort"]( some_table, sorting_object );``. +Furthermore, it is important to know that lambdas without a specified return type (and a non-const, non-reference-qualified ``auto``) will decay return values. To capture or return references explicitly, use ``decltype(auto)`` or specify the return type **exactly** as desired:: + + #define SOL_CHECK_ARGUMENTS 1 + #include + + int main(int argc, char* argv[]) { + + struct test { + int blah = 0; + }; + + test t; + sol::state lua; + lua.set_function("f", [&t]() { + return t; + }); + lua.set_function("g", [&t]() -> test& { + return t; + }); + + lua.script("t1 = f()"); + lua.script("t2 = g()"); + + test& lt1 = lua["t1"]; + test& lt2 = lua["t2"]; + + assert(<1 != &t); // not the same: 'f' lambda copied + assert(<2 == &t); // the same: 'g' lambda returned reference + + return 0; + } + .. _function-exception-handling: exception safety/handling diff --git a/examples/dynamic_object.cpp b/examples/dynamic_object.cpp new file mode 100644 index 00000000..3f438328 --- /dev/null +++ b/examples/dynamic_object.cpp @@ -0,0 +1,84 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include +#include + +// use as-is, +// add as a member of your class, +// or derive from it and bind it appropriately +struct dynamic_object { + std::unordered_map entries; + + void dynamic_set(std::string key, sol::stack_object value) { + auto it = entries.find(key); + if (it == entries.cend()) { + entries.insert(it, { std::move(key), std::move(value) }); + } + else { + std::pair& kvp = *it; + sol::object& entry = kvp.second; + entry = sol::object(std::move(value)); + } + } + + sol::object dynamic_get(std::string key) { + auto it = entries.find(key); + if (it == entries.cend()) { + return sol::lua_nil; + } + return it->second; + } +}; + + +int main() { + std::cout << "=== dynamic_object example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("dynamic_object", + sol::meta_function::index, &dynamic_object::dynamic_get, + sol::meta_function::new_index, &dynamic_object::dynamic_set, + sol::meta_function::length, [](dynamic_object& d) { + return d.entries.size(); + } + ); + + lua.safe_script(R"( +d1 = dynamic_object.new() +d2 = dynamic_object.new() + +print(#d1) -- length operator +print(#d2) + +function d2:run(lim) + local r = 0 + for i=0,lim do + r = r + i + end + if (r % 2) == 1 then + print("odd") + end + return r +end + +-- only added an entry to d2 +print(#d1) +print(#d2) + +-- only works on d2 +local value = d2:run(5) +assert(value == 15) +)"); + + // does not work on d1: 'run' wasn't added to d1, only d2 + auto script_result = lua.safe_script("local value = d1:run(5)", sol::script_pass_on_error); + assert(!script_result.valid()); + sol::error err = script_result; + std::cout << "received error: " << err.what() << std::endl; + std::cout << std::endl; + + return 0; +} \ No newline at end of file