diff --git a/docs/source/tutorial/all-the-things.rst b/docs/source/tutorial/all-the-things.rst index 686ce2af..df6d08aa 100644 --- a/docs/source/tutorial/all-the-things.rst +++ b/docs/source/tutorial/all-the-things.rst @@ -72,6 +72,9 @@ You can use the individual load and function call operator to load, check, and t :linenos: :lines: 1-10, 16-40, 47-49 +You can also `develop custom loaders`_ that pull from things that are not strings or files. + + set and get variables --------------------- @@ -271,6 +274,7 @@ Some more things you can do/read about: * :doc:`stack references<../api/stack_reference>` to have zero-overhead Sol abstractions while not copying to the Lua registry. * :doc:`resolve<../api/resolve>` overloads in case you have overloaded functions; a cleaner casting utility. You must use this to emulate default parameters. +.. _develop custom loaders: https://github.com/ThePhD/sol2/blob/develop/examples/custom_reader.cpp .. _basic example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype.cpp .. _special functions example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_special_functions.cpp .. _initializers example: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_initializers.cpp diff --git a/examples/custom_reader.cpp b/examples/custom_reader.cpp new file mode 100644 index 00000000..00485fdb --- /dev/null +++ b/examples/custom_reader.cpp @@ -0,0 +1,95 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include +#include +#include + +struct custom_reader { + FILE* f; + // We use 2 here to demonstrate + // multiple calls to the function to + // parse whole file. + // Please don't use 2. + // PLEASE DO NOT USE A BUFFER + // OF SIZE 2! + char buffer[2]; + std::size_t current_size; + std::size_t read_count; + + custom_reader(FILE* f_) : f(f_), buffer(), current_size(0), read_count(0) { + + } + + bool read() { + std::cout << "custom read: read #" << ++read_count << std::endl; + current_size = fread( buffer, 1, 2, f ); + return current_size > 0 && ferror(f) == 0; + } + + ~custom_reader( ) { + std::fclose( f ); + } +}; + +// function must match signature found in type lua_Reader: +// const char* ( lua_State*, void*, size_t* ) +const char* custom_reader_function(lua_State*, void* pointer_to_my_object, size_t* data_size) { + custom_reader& cr = *(static_cast(pointer_to_my_object)); + if ( cr.read( ) ) { + *data_size = cr.current_size; + return cr.buffer; + } + else { + *data_size = 0; + return nullptr; + } +} + +int main( ) { + std::cout << "=== custom reader ===" << std::endl; + + // make a file to use for the custom reader + { + std::ofstream bjork("bjork.lua", std::ios::binary); + bjork << "print('hello!')\n"; + } + struct on_scope_exit { + ~on_scope_exit() { + // remove file when done + std::remove("bjork.lua"); + } + } remove_on_exit; + + + sol::state lua; + lua.open_libraries( sol::lib::base ); + + FILE* bjork_fp; +#ifdef _MSC_VER + if ( fopen_s( &bjork_fp, "bjork.lua", "r" ) != 0 ) { + std::cerr << "failed to open bjork.lua -- exiting" << std::endl; + return -1; + } +#else + bjork_fp = fopen( "bjork.lua", "r" ); +#endif + if (bjork_fp == nullptr) { + std::cerr << "failed to open bjork.lua -- exiting" << std::endl; + return -1; + } + custom_reader reader(bjork_fp); + + // load the code using our custom reader, then run it + auto result = lua.safe_script( custom_reader_function, &reader, sol::script_pass_on_error ); + // make sure we ran loaded and ran the code successfully + c_assert( result.valid( ) ); + + // note there are lua.load( ... ) variants that take a + // custom reader than JUST run the code, too! + + std::cout << std::endl; + return 0; +} diff --git a/examples/require_override_behavior.cpp b/examples/require_override_behavior.cpp new file mode 100644 index 00000000..b48ea6ab --- /dev/null +++ b/examples/require_override_behavior.cpp @@ -0,0 +1,76 @@ +// Thanks to OrfeasZ for their answer to +// an issue for this example! +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include +#include + +// Use raw function of form "int(lua_State*)" +// -- this is called a "raw C function", +// and matches the type for lua_CFunction +int LoadFileRequire(lua_State* L) { + // use sol2 stack API to pull + // "first argument" + std::string path = sol::stack::get(L, 1); + + if (path == "a") { + std::string script = R"( + print("Hello from module land!") + test = 123 + return "bananas" + )"; + // load "module", but don't run it + luaL_loadbuffer(L, script.data(), script.size(), path.c_str()); + // returning 1 object left on Lua stack: + // a function that, when called, executes the script + // (this is what lua_loadX/luaL_loadX functions return + return 1; + } + + sol::stack::push(L, "This is not the module you're looking for!"); + return 1; +} + +int main() { + std::cout << "=== require override behavior ===" << std::endl; + + sol::state lua; + // need base for print, + // need package for package/searchers/require + lua.open_libraries(sol::lib::base, sol::lib::package); + + lua.clear_package_loaders( ); + lua.add_package_loader(LoadFileRequire); + + // this will call our function for + // the searcher and it will succeed + auto a_result = lua.safe_script(R"( + local a = require("a") + print(a) + print(test) + )", sol::script_pass_on_error); + c_assert(a_result.valid()); + try { + // this will always fail + auto b_result = lua.safe_script(R"( + local b = require("b") + print(b) + )", sol::script_throw_on_error); + // this will not be executed because of the throw, + // but it better be true regardless! + c_assert(!b_result.valid()); + } + catch (const std::exception& ex) { + // Whenever sol2 throws an exception from panic, + // catch + std::cout << "Something went wrong, as expected:\n" << ex.what() << std::endl; + // and CRASH / exit the application + return 0; + } + + // If we get here something went wrong...! + return -1; +} diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index b720bd82..ff60983d 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -332,7 +332,7 @@ namespace sol { return; } mustindex = true; - (void)detail::swallow{0, ((detail::has_derived::value = true), 0)...}; + //(void)detail::swallow{0, ((detail::has_derived::value = true), 0)...}; static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report."); diff --git a/sol/stack.hpp b/sol/stack.hpp index 1d5fb801..97697ca4 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -220,6 +220,14 @@ namespace sol { return call_syntax::colon; } + inline void script(lua_State* L, lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + detail::typical_chunk_name_t basechunkname = {}; + const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); + if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) { + lua_error(L); + } + } + inline void script(lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); diff --git a/sol/stack_push.hpp b/sol/stack_push.hpp index 7c92a727..67473929 100644 --- a/sol/stack_push.hpp +++ b/sol/stack_push.hpp @@ -169,7 +169,7 @@ namespace stack { detail::unique_tag* id = nullptr; Real* mem = detail::usertype_unique_allocate(L, pref, fx, id); *fx = detail::usertype_unique_alloc_destroy; - *id = &detail::inheritance

::type_unique_cast; + *id = &detail::inheritance

::template type_unique_cast; detail::default_construct::construct(mem, std::forward(args)...); *pref = unique_usertype_traits::get(*mem); if (luaL_newmetatable(L, &usertype_traits>>::metatable()[0]) == 1) { diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 93a7d08a..5116ae97 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -217,11 +217,74 @@ namespace sol { return require_core(key, action, create_global); } + void clear_package_loaders( ) { + optional maybe_package = this->global["package"]; + if (!maybe_package) { + // package lib wasn't opened + // open package lib + return; + } + table& package = *maybe_package; + // yay for version differences... + // one day Lua 5.1 will die a peaceful death + // and its old bones will find blissful rest + auto loaders_proxy = package +#if SOL_LUA_VERSION < 502 + ["loaders"] +#else + ["searchers"] +#endif + ; + if (!loaders_proxy.valid()) { + // nothing to clear + return; + } + // we need to create the table for loaders + // table does not exist, so create and move forward + loaders_proxy = new_table(1, 0); + } + + template + void add_package_loader(Fx&& fx, bool clear_all_package_loaders = false) { + optional
maybe_package = this->global["package"]; + if (!maybe_package) { + // package lib wasn't opened + // open package lib + return; + } + table& package = *maybe_package; + // yay for version differences... + // one day Lua 5.1 will die a peaceful death + // and its old bones will find blissful rest + auto loaders_proxy = package +#if SOL_LUA_VERSION < 502 + ["loaders"] +#else + ["searchers"] +#endif + ; + bool make_new_table = clear_all_package_loaders || !loaders_proxy.valid( ); + if (make_new_table) { + // we need to create the table for loaders + // table does not exist, so create and move forward + loaders_proxy = new_table(1, 0); + } + optional
maybe_loaders = loaders_proxy; + if (!maybe_loaders) { + // loaders/searches + // thing exists in package, but it + // ain't a table or a table-alike...! + return; + } + table loaders = loaders_proxy; + loaders.add(std::forward(fx)); + } + template - protected_function_result do_string(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result do_reader(lua_Reader reader, void* data, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; - const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); - load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); + const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); + load_status x = static_cast(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } @@ -230,9 +293,22 @@ namespace sol { return pf(); } + protected_function_result do_reader(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + detail::typical_chunk_name_t basechunkname = {}; + const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); + load_status x = static_cast(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())); + if (x != load_status::ok) { + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); + } + stack_aligned_protected_function pf(L, -1); + return pf(); + } + template - protected_function_result do_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { - load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); + protected_function_result do_string(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + detail::typical_chunk_name_t basechunkname = {}; + const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname); + load_status x = static_cast(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())); if (x != load_status::ok) { return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); } @@ -252,6 +328,17 @@ namespace sol { return pf(); } + template + protected_function_result do_file(const std::string& filename, const basic_environment& env, load_mode mode = load_mode::any) { + load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); + if (x != load_status::ok) { + return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast(x)); + } + stack_aligned_protected_function pf(L, -1); + set_environment(env, pf); + return pf(); + } + protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) { load_status x = static_cast(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())); if (x != load_status::ok) { @@ -261,6 +348,19 @@ namespace sol { return pf(); } + template >, meta::is_specialization_of, basic_environment>> = meta::enabler> + protected_function_result safe_script(lua_Reader reader, void* data, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + protected_function_result pfr = do_reader(reader, data, chunkname, mode); + if (!pfr.valid()) { + return on_error(L, std::move(pfr)); + } + return pfr; + } + + protected_function_result safe_script(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(reader, data, script_default_on_error, chunkname, mode); + } + template >, meta::is_specialization_of, basic_environment>> = meta::enabler> protected_function_result safe_script(const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { protected_function_result pfr = do_string(code, chunkname, mode); @@ -315,6 +415,31 @@ namespace sol { return safe_script_file(filename, script_default_on_error, mode); } + template + unsafe_function_result unsafe_script(lua_Reader reader, void* data, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + detail::typical_chunk_name_t basechunkname = {}; + const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname); + int index = lua_gettop(L); + if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())) { + lua_error(L); + } + set_environment(env, stack_reference(L, raw_index(index + 1))); + if (lua_pcall(L, 0, LUA_MULTRET, 0)) { + lua_error(L); + } + int postindex = lua_gettop(L); + int returns = postindex - index; + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + } + + unsafe_function_result unsafe_script(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + int index = lua_gettop(L); + stack::script(L, reader, data, chunkname, mode); + int postindex = lua_gettop(L); + int returns = postindex - index; + return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns); + } + template unsafe_function_result unsafe_script(const string_view& code, const basic_environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { detail::typical_chunk_name_t basechunkname = {}; @@ -392,6 +517,10 @@ namespace sol { } #if defined(SOL_SAFE_FUNCTION) && SOL_SAFE_FUNCTION + protected_function_result script(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { + return safe_script(reader, data, chunkname, mode); + } + protected_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) { return safe_script(code, chunkname, mode); } @@ -448,6 +577,12 @@ namespace sol { } global_table globals() const { + // if we return a reference + // we'll be screwed a bit + return global; + } + + global_table& globals() { return global; } diff --git a/tests/test_customizations.cpp b/tests/test_customizations.cpp index cafe140e..71d75d68 100644 --- a/tests/test_customizations.cpp +++ b/tests/test_customizations.cpp @@ -147,7 +147,7 @@ TEST_CASE("customization/split struct", "using the newly documented customizatio REQUIRE(d == 36.5); } -TEST_CASE("customization/get_ check_usertype", "using the newly documented customization points to handle different kinds of classes") { +TEST_CASE("customization/usertype", "using the newly documented customization points to handle different kinds of classes") { sol::state lua; // Create a pass-through style of function @@ -168,3 +168,17 @@ TEST_CASE("customization/get_ check_usertype", "using the newly documented custo REQUIRE(thingsf.num == 25); REQUIRE(thingsg.num == 35); } + +TEST_CASE("customization/overloading", "using multi-size customized types in an overload") { + bool TwoThingsWorks = false, OverloadWorks = false; + sol::state lua; + lua["test_two_things"] = [&](two_things) { TwoThingsWorks = true; }; + lua["test_overload"] = sol::overload([&](two_things) { OverloadWorks = true; }, [] {}); + + lua.script( + "test_two_things(0, true)\n" + "test_overload(0, true)"); + + REQUIRE(TwoThingsWorks); + REQUIRE(OverloadWorks); +}