diff --git a/apps/openmw_test_suite/lua/test_lua.cpp b/apps/openmw_test_suite/lua/test_lua.cpp index 76d430b440..90c987522d 100644 --- a/apps/openmw_test_suite/lua/test_lua.cpp +++ b/apps/openmw_test_suite/lua/test_lua.cpp @@ -100,6 +100,23 @@ return { EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), "something")), "\"something\""); } + TEST_F(LuaStateTest, Cast) + { + EXPECT_EQ(LuaUtil::cast(sol::make_object(mLua.sol(), 3.14)), 3); + EXPECT_ERROR( + LuaUtil::cast(sol::make_object(mLua.sol(), "3.14")), "Value \"\"3.14\"\" can not be casted to int"); + EXPECT_ERROR(LuaUtil::cast(sol::make_object(mLua.sol(), sol::nil)), + "Value \"nil\" can not be casted to string"); + EXPECT_ERROR(LuaUtil::cast(sol::make_object(mLua.sol(), sol::nil)), + "Value \"nil\" can not be casted to string"); + EXPECT_ERROR(LuaUtil::cast(sol::make_object(mLua.sol(), sol::nil)), + "Value \"nil\" can not be casted to sol::table"); + EXPECT_ERROR(LuaUtil::cast(sol::make_object(mLua.sol(), "3.14")), + "Value \"\"3.14\"\" can not be casted to sol::function"); + EXPECT_ERROR(LuaUtil::cast(sol::make_object(mLua.sol(), "3.14")), + "Value \"\"3.14\"\" can not be casted to sol::function"); + } + TEST_F(LuaStateTest, ErrorHandling) { EXPECT_ERROR(mLua.runInNewSandbox("invalid.lua"), "[string \"invalid.lua\"]:1:"); diff --git a/components/lua/luastate.cpp b/components/lua/luastate.cpp index 453e9d1586..a8818ceee0 100644 --- a/components/lua/luastate.cpp +++ b/components/lua/luastate.cpp @@ -420,4 +420,25 @@ namespace LuaUtil return call(sol::state_view(obj.lua_state())["tostring"], obj); } + std::string internal::formatCastingError(const sol::object& obj, const std::type_info& t) + { + const char* typeName = t.name(); + if (t == typeid(int)) + typeName = "int"; + else if (t == typeid(unsigned)) + typeName = "uint32"; + else if (t == typeid(size_t)) + typeName = "size_t"; + else if (t == typeid(float)) + typeName = "float"; + else if (t == typeid(double)) + typeName = "double"; + else if (t == typeid(sol::table)) + typeName = "sol::table"; + else if (t == typeid(sol::function) || t == typeid(sol::protected_function)) + typeName = "sol::function"; + else if (t == typeid(std::string) || t == typeid(std::string_view)) + typeName = "string"; + return std::string("Value \"") + toString(obj) + std::string("\" can not be casted to ") + typeName; + } } diff --git a/components/lua/luastate.hpp b/components/lua/luastate.hpp index a41a29d283..aea1e32590 100644 --- a/components/lua/luastate.hpp +++ b/components/lua/luastate.hpp @@ -1,12 +1,12 @@ #ifndef COMPONENTS_LUA_LUASTATE_H #define COMPONENTS_LUA_LUASTATE_H +#include #include +#include #include -#include - #include "configuration.hpp" namespace VFS @@ -247,15 +247,25 @@ namespace LuaUtil // String representation of a Lua object. Should be used for debugging/logging purposes only. std::string toString(const sol::object&); + namespace internal + { + std::string formatCastingError(const sol::object& obj, const std::type_info&); + } + + template + decltype(auto) cast(const sol::object& obj) + { + if (!obj.is()) + throw std::runtime_error(internal::formatCastingError(obj, typeid(T))); + return obj.as(); + } + template T getValueOrDefault(const sol::object& obj, const T& defaultValue) { if (obj == sol::nil) return defaultValue; - if (obj.is()) - return obj.as(); - else - throw std::logic_error(std::string("Value \"") + toString(obj) + std::string("\" has unexpected type")); + return cast(obj); } // Makes a table read only (when accessed from Lua) by wrapping it with an empty userdata.