From 4acc4a40888dd034e4c7ee08c3947df0444bb228 Mon Sep 17 00:00:00 2001 From: Max Roncace Date: Wed, 10 Jul 2024 21:30:08 -0400 Subject: [PATCH] scripting: Add support for returning Result types from bound functions --- .../src/renderer/2d/object_proc_impl.cpp | 2 + .../src/renderer/bucket_proc.cpp | 2 + .../src/renderer/compositing.cpp | 2 + .../src/renderer/texture_mgmt.cpp | 2 + .../src/state/renderer_state.cpp | 2 + .../src/renderer/2d/object_proc_impl.cpp | 2 + .../src/renderer/bucket_proc.cpp | 2 + .../src/renderer/compositing.cpp | 2 + .../src/renderer/texture_mgmt.cpp | 2 + .../src/state/renderer_state.cpp | 2 + .../src/renderer/2d/object_proc_impl.cpp | 2 + .../src/renderer/bucket_proc.cpp | 2 + .../src/renderer/compositing.cpp | 2 + .../src/renderer/texture_mgmt.cpp | 2 + .../src/state/renderer_state.cpp | 2 + .../render_vulkan/src/setup/device.cpp | 2 + .../include/argus/lowlevel/result.hpp | 20 ++ .../game2d/include/argus/game2d/world2d.hpp | 1 + engine/static/game2d/src/script_bindings.cpp | 2 +- .../include/argus/scripting/bind.hpp | 88 +++++-- .../include/argus/scripting/object_type.hpp | 9 + .../include/argus/scripting/types.hpp | 51 +++- .../include/argus/scripting/wrapper.hpp | 75 ++++-- .../internal/scripting/lowlevel_bindings.hpp | 2 - engine/static/scripting/src/bind.cpp | 23 +- engine/static/scripting/src/types.cpp | 104 ++++++--- engine/static/scripting/src/wrapper.cpp | 44 +++- .../internal/scripting_lua/defines.hpp | 8 + .../scripting_lua/src/lua_language_plugin.cpp | 221 +++++++++++++++++- engine/static/wm/include/argus/wm/display.hpp | 3 +- engine/static/wm/include/argus/wm/window.hpp | 3 +- .../wm/include/argus/wm/window_event.hpp | 3 +- engine/static/wm/src/api_util.cpp | 2 + 33 files changed, 582 insertions(+), 109 deletions(-) diff --git a/engine/dynamic/render_opengl/src/renderer/2d/object_proc_impl.cpp b/engine/dynamic/render_opengl/src/renderer/2d/object_proc_impl.cpp index d1f3329e..2b2304f5 100644 --- a/engine/dynamic/render_opengl/src/renderer/2d/object_proc_impl.cpp +++ b/engine/dynamic/render_opengl/src/renderer/2d/object_proc_impl.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/defines.hpp" diff --git a/engine/dynamic/render_opengl/src/renderer/bucket_proc.cpp b/engine/dynamic/render_opengl/src/renderer/bucket_proc.cpp index 8fcf3a0c..f20fb127 100644 --- a/engine/dynamic/render_opengl/src/renderer/bucket_proc.cpp +++ b/engine/dynamic/render_opengl/src/renderer/bucket_proc.cpp @@ -18,6 +18,8 @@ #include "argus/lowlevel/debug.hpp" +#include "argus/core/engine.hpp" + #include "argus/render/defines.hpp" #include "internal/render_opengl/defines.hpp" diff --git a/engine/dynamic/render_opengl/src/renderer/compositing.cpp b/engine/dynamic/render_opengl/src/renderer/compositing.cpp index 0df4af96..887a7de5 100644 --- a/engine/dynamic/render_opengl/src/renderer/compositing.cpp +++ b/engine/dynamic/render_opengl/src/renderer/compositing.cpp @@ -20,6 +20,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/common/canvas.hpp" diff --git a/engine/dynamic/render_opengl/src/renderer/texture_mgmt.cpp b/engine/dynamic/render_opengl/src/renderer/texture_mgmt.cpp index e99f9f9a..0d930de6 100644 --- a/engine/dynamic/render_opengl/src/renderer/texture_mgmt.cpp +++ b/engine/dynamic/render_opengl/src/renderer/texture_mgmt.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/logging.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource.hpp" #include "argus/resman/resource_manager.hpp" diff --git a/engine/dynamic/render_opengl/src/state/renderer_state.cpp b/engine/dynamic/render_opengl/src/state/renderer_state.cpp index a6996e7a..f7ff6c2a 100644 --- a/engine/dynamic/render_opengl/src/state/renderer_state.cpp +++ b/engine/dynamic/render_opengl/src/state/renderer_state.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include "argus/core/engine.hpp" + #include "argus/render/common/scene.hpp" #include "internal/render_opengl/renderer/shader_mgmt.hpp" diff --git a/engine/dynamic/render_opengl_legacy/src/renderer/2d/object_proc_impl.cpp b/engine/dynamic/render_opengl_legacy/src/renderer/2d/object_proc_impl.cpp index 814cfef7..b4c2c727 100644 --- a/engine/dynamic/render_opengl_legacy/src/renderer/2d/object_proc_impl.cpp +++ b/engine/dynamic/render_opengl_legacy/src/renderer/2d/object_proc_impl.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/defines.hpp" diff --git a/engine/dynamic/render_opengl_legacy/src/renderer/bucket_proc.cpp b/engine/dynamic/render_opengl_legacy/src/renderer/bucket_proc.cpp index 507c12e5..c8bdfadb 100644 --- a/engine/dynamic/render_opengl_legacy/src/renderer/bucket_proc.cpp +++ b/engine/dynamic/render_opengl_legacy/src/renderer/bucket_proc.cpp @@ -18,6 +18,8 @@ #include "argus/lowlevel/debug.hpp" +#include "argus/core/engine.hpp" + #include "argus/render/defines.hpp" #include "internal/render_opengl_legacy/defines.hpp" diff --git a/engine/dynamic/render_opengl_legacy/src/renderer/compositing.cpp b/engine/dynamic/render_opengl_legacy/src/renderer/compositing.cpp index e6d0a013..8aa90fbe 100644 --- a/engine/dynamic/render_opengl_legacy/src/renderer/compositing.cpp +++ b/engine/dynamic/render_opengl_legacy/src/renderer/compositing.cpp @@ -20,6 +20,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/common/canvas.hpp" diff --git a/engine/dynamic/render_opengl_legacy/src/renderer/texture_mgmt.cpp b/engine/dynamic/render_opengl_legacy/src/renderer/texture_mgmt.cpp index 2bdf9a1d..e536c387 100644 --- a/engine/dynamic/render_opengl_legacy/src/renderer/texture_mgmt.cpp +++ b/engine/dynamic/render_opengl_legacy/src/renderer/texture_mgmt.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/logging.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource.hpp" #include "argus/resman/resource_manager.hpp" diff --git a/engine/dynamic/render_opengl_legacy/src/state/renderer_state.cpp b/engine/dynamic/render_opengl_legacy/src/state/renderer_state.cpp index 9913e9c0..d07bf9aa 100644 --- a/engine/dynamic/render_opengl_legacy/src/state/renderer_state.cpp +++ b/engine/dynamic/render_opengl_legacy/src/state/renderer_state.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include "argus/core/engine.hpp" + #include "argus/render/common/scene.hpp" #include "internal/render_opengl_legacy/renderer/shader_mgmt.hpp" diff --git a/engine/dynamic/render_opengles/src/renderer/2d/object_proc_impl.cpp b/engine/dynamic/render_opengles/src/renderer/2d/object_proc_impl.cpp index a9c29784..6118d76c 100644 --- a/engine/dynamic/render_opengles/src/renderer/2d/object_proc_impl.cpp +++ b/engine/dynamic/render_opengles/src/renderer/2d/object_proc_impl.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/defines.hpp" diff --git a/engine/dynamic/render_opengles/src/renderer/bucket_proc.cpp b/engine/dynamic/render_opengles/src/renderer/bucket_proc.cpp index a1e96032..b70cc650 100644 --- a/engine/dynamic/render_opengles/src/renderer/bucket_proc.cpp +++ b/engine/dynamic/render_opengles/src/renderer/bucket_proc.cpp @@ -18,6 +18,8 @@ #include "argus/lowlevel/debug.hpp" +#include "argus/core/engine.hpp" + #include "argus/render/defines.hpp" #include "internal/render_opengles/defines.hpp" diff --git a/engine/dynamic/render_opengles/src/renderer/compositing.cpp b/engine/dynamic/render_opengles/src/renderer/compositing.cpp index ad59c533..84fc7ade 100644 --- a/engine/dynamic/render_opengles/src/renderer/compositing.cpp +++ b/engine/dynamic/render_opengles/src/renderer/compositing.cpp @@ -20,6 +20,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource_manager.hpp" #include "argus/render/common/canvas.hpp" diff --git a/engine/dynamic/render_opengles/src/renderer/texture_mgmt.cpp b/engine/dynamic/render_opengles/src/renderer/texture_mgmt.cpp index dfd75b9a..bbaab9e4 100644 --- a/engine/dynamic/render_opengles/src/renderer/texture_mgmt.cpp +++ b/engine/dynamic/render_opengles/src/renderer/texture_mgmt.cpp @@ -19,6 +19,8 @@ #include "argus/lowlevel/debug.hpp" #include "argus/lowlevel/logging.hpp" +#include "argus/core/engine.hpp" + #include "argus/resman/resource.hpp" #include "argus/resman/resource_manager.hpp" diff --git a/engine/dynamic/render_opengles/src/state/renderer_state.cpp b/engine/dynamic/render_opengles/src/state/renderer_state.cpp index b2333fea..b4a6ea63 100644 --- a/engine/dynamic/render_opengles/src/state/renderer_state.cpp +++ b/engine/dynamic/render_opengles/src/state/renderer_state.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include "argus/core/engine.hpp" + #include "argus/render/common/scene.hpp" #include "internal/render_opengles/renderer/shader_mgmt.hpp" diff --git a/engine/dynamic/render_vulkan/src/setup/device.cpp b/engine/dynamic/render_vulkan/src/setup/device.cpp index 9341aeb8..2854dce6 100644 --- a/engine/dynamic/render_vulkan/src/setup/device.cpp +++ b/engine/dynamic/render_vulkan/src/setup/device.cpp @@ -20,6 +20,8 @@ #include "argus/lowlevel/logging.hpp" #include "argus/lowlevel/macros.hpp" +#include "argus/core/engine.hpp" + #include "internal/render_vulkan/module_render_vulkan.hpp" #include "internal/render_vulkan/setup/device.hpp" #include "internal/render_vulkan/setup/queues.hpp" diff --git a/engine/libs/lowlevel/include/argus/lowlevel/result.hpp b/engine/libs/lowlevel/include/argus/lowlevel/result.hpp index 98d76dd2..136b99de 100644 --- a/engine/libs/lowlevel/include/argus/lowlevel/result.hpp +++ b/engine/libs/lowlevel/include/argus/lowlevel/result.hpp @@ -27,6 +27,26 @@ namespace argus { template class Result; + template + struct is_result : std::false_type { + }; + + template + struct is_result> : std::true_type { + }; + + template + constexpr bool is_result_v = is_result::value; + + template + struct result_traits; + + template + struct result_traits> { + using value_type = T; + using error_type = E; + }; + template struct has_to_string_fn : std::false_type { }; diff --git a/engine/static/game2d/include/argus/game2d/world2d.hpp b/engine/static/game2d/include/argus/game2d/world2d.hpp index 3d34296c..0d073abc 100644 --- a/engine/static/game2d/include/argus/game2d/world2d.hpp +++ b/engine/static/game2d/include/argus/game2d/world2d.hpp @@ -19,6 +19,7 @@ #pragma once #include "argus/lowlevel/math.hpp" +#include "argus/lowlevel/misc.hpp" #include "argus/lowlevel/result.hpp" #include "argus/render/2d/camera_2d.hpp" diff --git a/engine/static/game2d/src/script_bindings.cpp b/engine/static/game2d/src/script_bindings.cpp index 9f0dd0cd..3dbc1b54 100644 --- a/engine/static/game2d/src/script_bindings.cpp +++ b/engine/static/game2d/src/script_bindings.cpp @@ -27,7 +27,7 @@ namespace argus { static void _bind_world_symbols(void) { bind_type("World2D").expect(); bind_member_static_function("create", &World2D::create).expect(); - bind_member_static_function("get", &World2D::get_or_crash).expect(); + bind_member_static_function("get", &World2D::get).expect(); bind_member_instance_function("get_id", &World2D::get_id).expect(); bind_member_instance_function("get_scale_factor", &World2D::get_scale_factor).expect(); bind_member_instance_function("get_camera_transform", &World2D::get_camera_transform).expect(); diff --git a/engine/static/scripting/include/argus/scripting/bind.hpp b/engine/static/scripting/include/argus/scripting/bind.hpp index 7162c422..2ba489d7 100644 --- a/engine/static/scripting/include/argus/scripting/bind.hpp +++ b/engine/static/scripting/include/argus/scripting/bind.hpp @@ -183,26 +183,72 @@ namespace argus { // _create_object_type is used to create function definitions // too so it doesn't attempt to resolve the type name - if (ret_obj_type.type == IntegralType::Pointer - || ret_obj_type.type == IntegralType::Struct) { - ret_obj_type.type_name = get_bound_type() - .expect("Tried to create function wrapper with unbound return struct type").name; - } else if (ret_obj_type.type == IntegralType::Enum) { - ret_obj_type.type_name = get_bound_enum() - .expect("Tried to create function wrapper with unbound return enum type").name; - } else if ((ret_obj_type.type == IntegralType::Vector || ret_obj_type.type == IntegralType::VectorRef) - && (ret_obj_type.element_type.value()->type == IntegralType::Struct - || ret_obj_type.element_type.value()->type == IntegralType::Pointer)) { - ret_obj_type.element_type.value()->type_name - = get_bound_type(ret_obj_type.element_type.value()->type_index.value()) - .expect("Tried to create function wrapper with vector return type and " - "unbound element struct type").name; - } else if ((ret_obj_type.type == IntegralType::Vector || ret_obj_type.type == IntegralType::VectorRef) - && ret_obj_type.element_type.value()->type == IntegralType::Enum) { - ret_obj_type.element_type.value()->type_name - = get_bound_enum(ret_obj_type.element_type.value()->type_index.value()) - .expect("Tried to create function wrapper with vector return type and " - "unbound element enum type").name; + //TODO: figure out if we can move this to the .cpp file + switch (ret_obj_type.type) { + case IntegralType::Pointer: + case IntegralType::Struct: { + ret_obj_type.type_name = get_bound_type() + .expect("Tried to create function wrapper with unbound return struct type").name; + + break; + } + case IntegralType::Enum: { + ret_obj_type.type_name = get_bound_enum() + .expect("Tried to create function wrapper with unbound return enum type").name; + + break; + } + case IntegralType::Vector: + case IntegralType::VectorRef: { + if (ret_obj_type.primary_type.value()->type == IntegralType::Struct + || ret_obj_type.primary_type.value()->type == IntegralType::Pointer) { + ret_obj_type.primary_type.value()->type_name + = get_bound_type(ret_obj_type.primary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with vector return type and " + "unbound element struct type").name; + } else if (ret_obj_type.primary_type.value()->type == IntegralType::Enum) { + ret_obj_type.primary_type.value()->type_name + = get_bound_enum(ret_obj_type.primary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with vector return type and " + "unbound element enum type").name; + } + + break; + } + case IntegralType::Result: { + argus_assert(ret_obj_type.primary_type.has_value()); + argus_assert(ret_obj_type.secondary_type.has_value()); + + if (ret_obj_type.primary_type.value()->type == IntegralType::Struct + || ret_obj_type.primary_type.value()->type == IntegralType::Pointer) { + ret_obj_type.primary_type.value()->type_name + = get_bound_type(ret_obj_type.primary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with result return type and " + "unbound value struct type").name; + } else if (ret_obj_type.primary_type.value()->type == IntegralType::Enum) { + ret_obj_type.primary_type.value()->type_name + = get_bound_type(ret_obj_type.primary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with result return type and " + "unbound value enum type").name; + } + + if (ret_obj_type.secondary_type.value()->type == IntegralType::Struct + || ret_obj_type.secondary_type.value()->type == IntegralType::Pointer) { + ret_obj_type.secondary_type.value()->type_name + = get_bound_type(ret_obj_type.secondary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with result return type and " + "unbound error struct type").name; + } else if (ret_obj_type.secondary_type.value()->type == IntegralType::Enum) { + ret_obj_type.secondary_type.value()->type_name + = get_bound_type(ret_obj_type.secondary_type.value()->type_index.value()) + .expect("Tried to create function wrapper with result return type and " + "unbound error enum type").name; + } + + break; + } + default: + break; // nothing to do } if constexpr (std::is_same_v>>>) { return create_vector_object_wrapper_from_heap(ret_obj_type, ret); + } else if constexpr (is_result_v>) { + return create_result_object_wrapper(ret_obj_type, ret); } else if constexpr (std::is_reference_v) { ObjectWrapper wrapper(ret_obj_type, ret_obj_size); diff --git a/engine/static/scripting/include/argus/scripting/object_type.hpp b/engine/static/scripting/include/argus/scripting/object_type.hpp index 513929b8..47caad79 100644 --- a/engine/static/scripting/include/argus/scripting/object_type.hpp +++ b/engine/static/scripting/include/argus/scripting/object_type.hpp @@ -103,6 +103,13 @@ namespace argus { using E = typename B::value_type; return { IntegralType::VectorRef, sizeof(void *), is_const, typeid(B), std::nullopt, std::nullopt, create_object_type() }; + } else if constexpr (is_result_v>) { + static_assert(flow_dir == DataFlowDirection::ToScript, + "Result types may not be passed or returned from scripts"); + using V = typename result_traits::value_type; + using E = typename result_traits::error_type; + return { IntegralType::Result, sizeof(T), is_const, typeid(T), std::nullopt, std::nullopt, + create_object_type(), create_object_type() }; } else if constexpr (std::is_same_v, bool>) { return { IntegralType::Boolean, sizeof(bool), is_const }; } else if constexpr (std::is_integral_v>) { @@ -118,6 +125,8 @@ namespace argus { } else if constexpr (std::is_reference_v || std::is_pointer_v>) { // too much of a headache to worry about static_assert(std::is_class_v, "Non-class reference params in bound functions are not supported"); + // no real use case for these, might as well simplify our implementation + static_assert(!is_result_v, "Result references in bound functions are not supported"); // References passed to scripts must be invalidated when the // underlying object is destroyed, which is only possible if the // type derives from AutoCleanupable. diff --git a/engine/static/scripting/include/argus/scripting/types.hpp b/engine/static/scripting/include/argus/scripting/types.hpp index 308e5a7f..c1b7f56b 100644 --- a/engine/static/scripting/include/argus/scripting/types.hpp +++ b/engine/static/scripting/include/argus/scripting/types.hpp @@ -54,7 +54,8 @@ namespace argus { Callback, Type, Vector, - VectorRef + VectorRef, + Result, }; enum class FunctionType { @@ -80,7 +81,8 @@ namespace argus { std::optional type_index = std::nullopt; std::optional type_name = std::nullopt; std::optional> callback_type = std::nullopt; - std::optional> element_type = std::nullopt; + std::optional> primary_type = std::nullopt; + std::optional> secondary_type = std::nullopt; ObjectType(void); @@ -91,7 +93,8 @@ namespace argus { std::optional type_index = std::nullopt, std::optional type_name = std::nullopt, std::optional> &&callback_type = std::nullopt, - std::optional element_type = std::nullopt + std::optional primary_type = std::nullopt, + std::optional secondary_type = std::nullopt ); ObjectType(const ObjectType &rhs); @@ -298,12 +301,46 @@ namespace argus { } }; + class ResultWrapper { + private: + size_t m_ok; + size_t m_size; + ObjectType m_resolved_type; + unsigned char m_blob[0]; + + public: + ResultWrapper(bool is_ok, size_t resolved_size, const ObjectType &resolved_type); + + explicit ResultWrapper(const ResultWrapper &rhs); + + explicit ResultWrapper(ResultWrapper &&rhs); + + ResultWrapper &operator=(const ResultWrapper &rhs) = delete; + + ResultWrapper &operator=(ResultWrapper &&rhs) = delete; + + [[nodiscard]] bool is_ok(void) const; + + [[nodiscard]] size_t get_size(void) const; + + [[nodiscard]] const ObjectType &get_value_or_error_type(void) const; + + [[nodiscard]] void *get_underlying_object_ptr(void); + + [[nodiscard]] const void *get_underlying_object_ptr(void) const; + + [[nodiscard]] Result to_object_wrapper(void) const; + + void copy_value_or_error_from(const void *src); + }; + template static void _validate_value_type(const ObjectType &type) { static_assert(!std::is_pointer_v, "Use reference type instead of pointer"); if (!(type.type == IntegralType::String || type.type == IntegralType::Pointer - || type.type == IntegralType::Vector || type.type == IntegralType::VectorRef)) { + || type.type == IntegralType::Vector || type.type == IntegralType::VectorRef + || type.type == IntegralType::Result)) { argus_assert(type.size == sizeof(T)); } @@ -358,6 +395,10 @@ namespace argus { argus_assert(std::is_same_v); break; } + case IntegralType::Result: { + argus_assert(std::is_same_v); + break; + } default: { crash("Unhandled IntegralType ordinal %d", type.type); } @@ -418,7 +459,7 @@ namespace argus { const T &get_value(void) const { argus_assert(is_initialized); - return reinterpret_cast(get_value()); + return reinterpret_cast(const_cast(this)->get_value()); } template diff --git a/engine/static/scripting/include/argus/scripting/wrapper.hpp b/engine/static/scripting/include/argus/scripting/wrapper.hpp index bfbd0333..6a48424d 100644 --- a/engine/static/scripting/include/argus/scripting/wrapper.hpp +++ b/engine/static/scripting/include/argus/scripting/wrapper.hpp @@ -47,50 +47,62 @@ namespace argus { template [[nodiscard]] Result get_bound_enum(void); - [[nodiscard]] Result create_object_wrapper(const ObjectType &type, const void *ptr); + [[nodiscard]] Result create_object_wrapper( + const ObjectType &type, const void *ptr); - [[nodiscard]] Result create_object_wrapper(const ObjectType &type, const void *ptr, + [[nodiscard]] Result create_object_wrapper( + const ObjectType &type, const void *ptr, size_t size); - [[nodiscard]] Result create_int_object_wrapper(const ObjectType &type, int64_t val); + [[nodiscard]] Result create_int_object_wrapper( + const ObjectType &type, int64_t val); - [[nodiscard]] Result create_float_object_wrapper(const ObjectType &type, double val); + [[nodiscard]] Result create_float_object_wrapper( + const ObjectType &type, double val); - [[nodiscard]] Result create_bool_object_wrapper(const ObjectType &type, bool val); + [[nodiscard]] Result create_bool_object_wrapper( + const ObjectType &type, bool val); - [[nodiscard]] Result create_enum_object_wrapper(const ObjectType &type, int64_t ordinal); + [[nodiscard]] Result create_enum_object_wrapper( + const ObjectType &type, int64_t ordinal); - [[nodiscard]] Result create_string_object_wrapper(const ObjectType &type, + [[nodiscard]] Result create_string_object_wrapper( + const ObjectType &type, const std::string &str); - [[nodiscard]] Result create_callback_object_wrapper(const ObjectType &type, + [[nodiscard]] Result create_callback_object_wrapper( + const ObjectType &type, const ProxiedScriptCallback &fn); - [[nodiscard]] Result create_vector_object_wrapper(const ObjectType &type, + [[nodiscard]] Result create_vector_object_wrapper( + const ObjectType &type, const void *data, size_t count); - [[nodiscard]] Result create_vector_object_wrapper(const ObjectType &vec_type, + [[nodiscard]] Result create_vector_object_wrapper( + const ObjectType &vec_type, const VectorWrapper &vec); - [[nodiscard]] Result create_vector_ref_object_wrapper(const ObjectType &vec_type, + [[nodiscard]] Result create_vector_ref_object_wrapper( + const ObjectType &vec_type, VectorWrapper vec); template::value_type, bool is_heap> - [[nodiscard]] Result _create_vector_object_wrapper(const ObjectType &type, V &vec) { + [[nodiscard]] Result _create_vector_object_wrapper( + const ObjectType &type, V &vec) { static_assert(!std::is_function_v && !is_std_function_v, "Vectors of callbacks are not supported"); static_assert(!is_std_vector_v, "Vectors of vectors are not supported"); static_assert(!std::is_same_v, "Vectors of booleans are not supported"); static_assert(!std::is_same_v>, char *>, "Vectors of C-strings are not supported (use std::string instead)"); - argus_assert(type.element_type.has_value()); + argus_assert(type.primary_type.has_value()); // ensure the vector reference will remain valid if (type.type == IntegralType::VectorRef && is_heap) { return create_vector_ref_object_wrapper(type, - VectorWrapper(const_cast &>(vec), *type.element_type.value())); + VectorWrapper(const_cast &>(vec), *type.primary_type.value())); } else { - if (type.element_type.value()->type != IntegralType::String) { - argus_assert(type.element_type.value()->size == sizeof(E)); + if (type.primary_type.value()->type != IntegralType::String) { + argus_assert(type.primary_type.value()->size == sizeof(E)); } ObjectType real_type = type; @@ -111,8 +123,26 @@ namespace argus { return _create_vector_object_wrapper(type, vec); } + Result create_result_object_wrapper(const ObjectType &res_type, + bool is_ok, const ObjectType &resolved_type, size_t resolved_size, void *resolved_ptr); + + template + [[nodiscard]] Result create_result_object_wrapper( + const ObjectType &res_type, Result &result) { + return create_result_object_wrapper( + res_type, + result.is_ok(), + *(result.is_ok() ? res_type.primary_type : res_type.secondary_type).value(), + result.is_ok() ? sizeof(T) : sizeof(E), + result.is_ok() + ? reinterpret_cast(&result.unwrap()) + : reinterpret_cast(&result.unwrap_err()) + ); + } + template - [[nodiscard]] Result create_auto_object_wrapper(const ObjectType &type, T val) { + [[nodiscard]] Result create_auto_object_wrapper( + const ObjectType &type, T val) { using B = std::remove_cv_t>>>; // It's possible for a script to pass a vector literal to a bound @@ -153,8 +183,8 @@ namespace argus { } template - [[nodiscard]] static Result, ReflectiveArgumentsError> _make_params_from_tuple_impl(ArgsTuple &tuple, - const std::vector::const_iterator &types_it, std::index_sequence) { + [[nodiscard]] static Result, ReflectiveArgumentsError> _make_params_from_tuple_impl( + ArgsTuple &tuple, const std::vector::const_iterator &types_it, std::index_sequence) { std::vector> results; (results.emplace_back(create_auto_object_wrapper>(*(types_it + Is), std::get(tuple))), ...); @@ -172,8 +202,8 @@ namespace argus { } template - [[nodiscard]] static Result, ReflectiveArgumentsError> _make_params_from_tuple(ArgsTuple &tuple, - const std::vector::const_iterator &types_it) { + [[nodiscard]] static Result, ReflectiveArgumentsError> _make_params_from_tuple( + ArgsTuple &tuple, const std::vector::const_iterator &types_it) { return _make_params_from_tuple_impl(tuple, types_it, std::make_index_sequence> {}); } @@ -292,7 +322,8 @@ namespace argus { const auto &wrapper = reinterpret_cast(obj); return wrapper.get_underlying_vector(); } else { - crash("Invalid vector object type magic"); + crash("Invalid vector object type magic %d", + std::underlying_type_t(obj.get_object_type())); } } else if constexpr (is_reference_wrapper_v) { argus_assert(param.type.type == IntegralType::Pointer); diff --git a/engine/static/scripting/include/internal/scripting/lowlevel_bindings.hpp b/engine/static/scripting/include/internal/scripting/lowlevel_bindings.hpp index e2e3a766..49c71d5d 100644 --- a/engine/static/scripting/include/internal/scripting/lowlevel_bindings.hpp +++ b/engine/static/scripting/include/internal/scripting/lowlevel_bindings.hpp @@ -20,8 +20,6 @@ #include "argus/lowlevel/time.hpp" -#include "argus/scripting/types.hpp" - #include namespace argus { diff --git a/engine/static/scripting/src/bind.cpp b/engine/static/scripting/src/bind.cpp index 2a760b35..bd1ec26a 100644 --- a/engine/static/scripting/src/bind.cpp +++ b/engine/static/scripting/src/bind.cpp @@ -54,8 +54,18 @@ namespace argus { return ok(); } else if (param_def.type == IntegralType::Vector || param_def.type == IntegralType::VectorRef) { - argus_assert(param_def.element_type.has_value()); - auto el_res = _resolve_param(*param_def.element_type.value(), false); + argus_assert(param_def.primary_type.has_value()); + auto el_res = _resolve_param(*param_def.primary_type.value(), false); + if (el_res.is_err()) { + return el_res; + } + + return ok(); + } else if (param_def.type == IntegralType::Result) { + argus_assert(param_def.primary_type.has_value()); + argus_assert(param_def.secondary_type.has_value()); + auto el_res = _resolve_param(*param_def.primary_type.value(), false) + .collate(_resolve_param(*param_def.secondary_type.value(), false)); if (el_res.is_err()) { return el_res; } @@ -117,8 +127,8 @@ namespace argus { [[nodiscard]] static Result _resolve_field(ObjectType &field_def) { if (field_def.type == IntegralType::Vector || field_def.type == IntegralType::VectorRef) { - argus_assert(field_def.element_type.has_value()); - auto el_res = _resolve_field(*field_def.element_type.value()); + argus_assert(field_def.primary_type.has_value()); + auto el_res = _resolve_field(*field_def.primary_type.value()); if (el_res.is_err()) { return el_res; } @@ -258,9 +268,8 @@ namespace argus { [[nodiscard]] Result _get_bound_type(const std::type_index type_index) { auto index_it = g_bound_type_indices.find(std::type_index(type_index)); if (index_it == g_bound_type_indices.cend()) { - crash("Type " + std::string(type_index.name()) - + " is not bound (check binding order and ensure bind_type" - " is called after creating type definition)"); + crash("Type %s is not bound (check binding order and ensure bind_type" + " is called after creating type definition)", type_index.name()); } auto type_it = g_bound_types.find(index_it->second); argus_assert(type_it != g_bound_types.cend()); diff --git a/engine/static/scripting/src/types.cpp b/engine/static/scripting/src/types.cpp index 49bfd8ef..45040078 100644 --- a/engine/static/scripting/src/types.cpp +++ b/engine/static/scripting/src/types.cpp @@ -23,8 +23,10 @@ #include "argus/scripting/types.hpp" #include "argus/scripting/wrapper.hpp" +#include #include #include +#include #include @@ -35,17 +37,22 @@ namespace argus { std::optional type_index, std::optional type_name, std::optional> &&callback_type, - std::optional element_type): + std::optional primary_type, + std::optional secondary_type): type(type), size(size), is_const(is_const), type_index(type_index), type_name(std::move(type_name)), callback_type(std::move(callback_type)), - element_type(element_type.has_value() + primary_type(primary_type.has_value() ? std::make_optional>( - std::make_unique(std::move(element_type.value()))) - : std::nullopt) { + std::make_unique(std::move(primary_type.value()))) + : std::nullopt), + secondary_type(secondary_type.has_value() + ? std::make_optional>( + std::make_unique(std::move(secondary_type.value()))) + : std::nullopt){ } @@ -59,9 +66,13 @@ namespace argus { ? std::make_optional>( std::make_unique(*rhs.callback_type.value())) : std::nullopt), - element_type(rhs.element_type.has_value() + primary_type(rhs.primary_type.has_value() + ? std::make_optional>( + std::make_unique(*rhs.primary_type.value())) + : std::nullopt), + secondary_type(rhs.secondary_type.has_value() ? std::make_optional>( - std::make_unique(*rhs.element_type.value())) + std::make_unique(*rhs.secondary_type.value())) : std::nullopt) { } @@ -72,38 +83,18 @@ namespace argus { type_index(rhs.type_index), type_name(std::move(rhs.type_name)), callback_type(std::move(rhs.callback_type)), - element_type(std::move(rhs.element_type)) { + primary_type(std::move(rhs.primary_type)), + secondary_type(std::move(rhs.secondary_type)) { } ObjectType::~ObjectType(void) = default; ObjectType &ObjectType::operator=(const ObjectType &rhs) { - type = rhs.type; - size = rhs.size; - is_const = rhs.is_const; - type_index = rhs.type_index; - type_name = rhs.type_name; - callback_type = rhs.callback_type.has_value() - ? std::make_optional>( - std::make_unique(*rhs.callback_type.value())) - : std::nullopt; - element_type = rhs.element_type.has_value() - ? std::make_optional>( - std::make_unique(*rhs.element_type.value())) - : std::nullopt; + new(this) ObjectType(rhs); return *this; } - ObjectType &ObjectType::operator=(ObjectType &&rhs) noexcept { - type = rhs.type; - size = rhs.size; - is_const = rhs.is_const; - type_index = rhs.type_index; - type_name = rhs.type_name; - callback_type = std::move(rhs.callback_type); - element_type = std::move(rhs.element_type); - return *this; - } + ObjectType &ObjectType::operator=(ObjectType &&rhs) noexcept = default; ObjectWrapper::ObjectWrapper(void): type(ObjectType { IntegralType::Void, 0 }), @@ -117,13 +108,16 @@ namespace argus { type(type), is_initialized(false) { argus_assert(type.type == IntegralType::String || type.type == IntegralType::Pointer - || type.type == IntegralType::Vector || type.type == IntegralType::VectorRef || type.size == size); + || type.type == IntegralType::Vector || type.type == IntegralType::VectorRef + || type.type == IntegralType::Result || type.size == size); // override size for pointer type since we're only copying the pointer size_t copy_size = type.type == IntegralType::Pointer ? sizeof(void *) - : type.type == IntegralType::String || type.type == IntegralType::Vector + : (type.type == IntegralType::String + || type.type == IntegralType::Vector || type.type == IntegralType::VectorRef + || type.type == IntegralType::Result) ? size : type.size; this->buffer_size = copy_size; @@ -183,7 +177,7 @@ namespace argus { } void ObjectWrapper::copy_value_from(const void *src, size_t size) { - argus_assert(size == this->buffer_size); + argus_assert(size >= this->buffer_size); copy_wrapped_object(this->type, this->get_ptr0(), src, size); this->is_initialized = true; } @@ -332,4 +326,48 @@ namespace argus { affirm_precond(!m_element_type.is_const, "Cannot mutate const vector via VectorWrapper"); (*m_set_element_fn)(m_underlying_vec, index, val); } + + ResultWrapper::ResultWrapper(bool is_ok, size_t resolved_size, const ObjectType &resolved_type): + m_ok(is_ok), + m_size(resolved_size), + m_resolved_type(resolved_type) { + } + + ResultWrapper::ResultWrapper(const ResultWrapper &rhs): + ResultWrapper(rhs.m_ok, rhs.m_size, rhs.m_resolved_type) { + copy_wrapped_object(rhs.m_resolved_type, this->m_blob, rhs.m_blob, rhs.m_size); + } + + ResultWrapper::ResultWrapper(ResultWrapper &&rhs): + ResultWrapper(rhs.m_ok, rhs.m_size, rhs.m_resolved_type) { + move_wrapped_object(rhs.m_resolved_type, this->m_blob, rhs.m_blob, rhs.m_size); + } + + bool ResultWrapper::is_ok(void) const { + return m_ok; + } + + size_t ResultWrapper::get_size(void) const { + return m_size; + } + + const ObjectType &ResultWrapper::get_value_or_error_type(void) const { + return m_resolved_type; + } + + void *ResultWrapper::get_underlying_object_ptr(void) { + return reinterpret_cast(m_blob); + } + + const void *ResultWrapper::get_underlying_object_ptr(void) const { + return reinterpret_cast(m_blob); + } + + Result ResultWrapper::to_object_wrapper(void) const { + return create_object_wrapper(m_resolved_type, m_blob, m_size); + } + + void ResultWrapper::copy_value_or_error_from(const void *src) { + copy_wrapped_object(m_resolved_type, m_blob, src, m_size); + } } diff --git a/engine/static/scripting/src/wrapper.cpp b/engine/static/scripting/src/wrapper.cpp index 0bc8b3a0..7abb46d5 100644 --- a/engine/static/scripting/src/wrapper.cpp +++ b/engine/static/scripting/src/wrapper.cpp @@ -135,7 +135,7 @@ namespace argus { } static void _validate_vec_obj_type(ObjectType vec_type) { - ObjectType &el_type = *vec_type.element_type.value(); + ObjectType &el_type = *vec_type.primary_type.value(); if (el_type.type == IntegralType::Void) { crash("Vectors of void are not supported"); @@ -161,7 +161,7 @@ namespace argus { _validate_vec_obj_type(vec_type); affirm_precond(count < SIZE_MAX, "Too many vector elements"); - ObjectType &el_type = *vec_type.element_type.value(); + ObjectType &el_type = *vec_type.primary_type.value(); bool is_trivially_copyable = el_type.type != IntegralType::String && !(el_type.type == IntegralType::Struct @@ -244,12 +244,31 @@ namespace argus { return ok(std::move(wrapper)); } + Result create_result_object_wrapper(const ObjectType &res_type, + bool is_ok, const ObjectType &resolved_type, size_t resolved_size, void *resolved_ptr) { + affirm_precond(res_type.type == IntegralType::Result, + "Cannot create object wrapper (result-specific overload called for non-result-typed value"); + + ObjectWrapper wrapper(res_type, sizeof(ResultWrapper) + resolved_size); + auto &res_wrapper = wrapper.emplace(is_ok, resolved_size, resolved_type); + + void *real_ptr; + if (resolved_type.type == IntegralType::Pointer) { + real_ptr = &resolved_ptr; + } else { + real_ptr = resolved_ptr; + } + + res_wrapper.copy_value_or_error_from(real_ptr); + return ok(wrapper); + } + template> static void _copy_or_move_array_blob(const ObjectType &obj_type, void *dst, T &src, size_t max_len) { auto el_size = src.element_size(); auto count = src.size(); - ObjectType &el_type = *obj_type.element_type.value(); + ObjectType &el_type = *obj_type.primary_type.value(); bool is_trivially_copyable = el_type.type != IntegralType::String && !(el_type.type == IntegralType::Struct @@ -286,6 +305,19 @@ namespace argus { } } + template> + static void _copy_or_move_result_wrapper(void *dst, T &src, size_t max_len) { + auto &dst_res = *new(dst) ResultWrapper(src.is_ok(), src.get_size(), src.get_value_or_error_type()); + + if constexpr (is_move) { + move_wrapped_object(src.get_value_or_error_type(), dst_res.get_underlying_object_ptr(), + src.get_underlying_object_ptr(), max_len); + } else { + copy_wrapped_object(src.get_value_or_error_type(), dst_res.get_underlying_object_ptr(), + src.get_underlying_object_ptr(), max_len); + } + } + template> static void _copy_or_move_type(void *dst, SrcPtr src, size_t max_len) { argus_assert(max_len >= sizeof(T)); @@ -356,6 +388,12 @@ namespace argus { break; } + case IntegralType::Result: { + auto &src_res = *reinterpret_cast *>(src); + _copy_or_move_result_wrapper(dst, src_res, size); + + break; + } default: { // for everything else we bitwise-copy the value // note that std::type_index is trivially copyable diff --git a/engine/static/scripting_lua/include/internal/scripting_lua/defines.hpp b/engine/static/scripting_lua/include/internal/scripting_lua/defines.hpp index 0360a694..a3bc5198 100644 --- a/engine/static/scripting_lua/include/internal/scripting_lua/defines.hpp +++ b/engine/static/scripting_lua/include/internal/scripting_lua/defines.hpp @@ -32,10 +32,18 @@ namespace argus { constexpr const char *k_lua_require = "require"; constexpr const char *k_lua_require_def = "default_require"; + constexpr const char *k_result_is_ok_fn = "is_ok"; + constexpr const char *k_result_is_err_fn = "is_err"; + constexpr const char *k_result_unwrap_fn = "unwrap"; + constexpr const char *k_result_unwrap_err_fn = "unwrap_err"; + constexpr const char *k_result_expect_fn = "expect"; + constexpr const char *k_result_expect_err_fn = "expect_err"; + constexpr const char *k_clone_fn = "clone"; constexpr const char *k_mt_vector = "_internal_vector"; constexpr const char *k_mt_vector_ref = "_internal_vectorref"; + constexpr const char *k_mt_result = "_internal_result"; constexpr const char *k_const_prefix = "const "; diff --git a/engine/static/scripting_lua/src/lua_language_plugin.cpp b/engine/static/scripting_lua/src/lua_language_plugin.cpp index 8ba7126f..6195ebe4 100644 --- a/engine/static/scripting_lua/src/lua_language_plugin.cpp +++ b/engine/static/scripting_lua/src/lua_language_plugin.cpp @@ -261,7 +261,7 @@ namespace argus { static Result _read_vector_from_table(lua_State *state, const std::string &qual_fn_name, int param_index, const ObjectType ¶m_def) { - const auto &element_type = *param_def.element_type.value(); + const auto &element_type = *param_def.primary_type.value(); // for simplicity's sake we require contiguous indices @@ -562,7 +562,7 @@ namespace argus { } case IntegralType::Vector: case IntegralType::VectorRef: { - argus_assert(param_def.element_type.has_value()); + argus_assert(param_def.primary_type.has_value()); if (lua_istable(state, param_index)) { return _read_vector_from_table(state, qual_fn_name, param_index, param_def); @@ -702,6 +702,178 @@ namespace argus { return 1; } + static int _lua_result_is_ok_handler(lua_State *state) { + StackGuard stack_guard(state); + + void *udata_ptr = lua_touserdata(state, -1); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + lua_pushboolean(state, res->is_ok()); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_is_err_handler(lua_State *state) { + StackGuard stack_guard(state); + + void *udata_ptr = lua_touserdata(state, -1); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + lua_pushboolean(state, !res->is_ok()); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_unwrap_handler(lua_State *state) { + StackGuard stack_guard(state); + + void *udata_ptr = lua_touserdata(state, -1); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + if (!res->is_ok()) { + int err_inc = luaL_error(state, "Cannot unwrap value from error-typed result"); + stack_guard.increment(err_inc); + return err_inc; + } + + _push_value(state, + res->to_object_wrapper().expect("Failed to create object wrapper while unwrapping result value")); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_unwrap_err_handler(lua_State *state) { + StackGuard stack_guard(state); + + void *udata_ptr = lua_touserdata(state, -1); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + if (res->is_ok()) { + int err_inc = luaL_error(state, "Cannot unwrap error from value-typed result"); + stack_guard.increment(err_inc); + return err_inc; + } + + _push_value(state, + res->to_object_wrapper().expect("Failed to create object wrapper while unwrapping result error")); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_expect_handler(lua_State *state) { + StackGuard stack_guard(state); + + auto arg_count = lua_gettop(state) - 1; + + void *udata_ptr = lua_touserdata(state, -1 - arg_count); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + if (!res->is_ok()) { + int err_inc = luaL_error(state, arg_count > 0 ? lua_tostring(state, -1) : "Expectation failed"); + stack_guard.increment(err_inc); + return err_inc; + } + + _push_value(state, + res->to_object_wrapper().expect("Failed to create object wrapper while unwrapping result error")); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_expect_err_handler(lua_State *state) { + StackGuard stack_guard(state); + + auto arg_count = lua_gettop(state) - 1; + + void *udata_ptr = lua_touserdata(state, -1 - arg_count); + + if (udata_ptr == nullptr) { + return luaL_error(state, "Result methods may not be statically invoked (use the colon operator instead)"); + } + + const ResultWrapper *res = reinterpret_cast(udata_ptr); + + if (res->is_ok()) { + int err_inc = luaL_error(state, arg_count > 0 ? lua_tostring(state, -1) : "Expectation failed"); + stack_guard.increment(err_inc); + return err_inc; + } + + _push_value(state, + res->to_object_wrapper().expect("Failed to create object wrapper while unwrapping result error")); + stack_guard.increment(); + + return 1; + } + + static int _lua_result_index_handler(lua_State *state) { + StackGuard guard(state); + + const char *method_name = lua_tostring(state, -1); + + if (strcmp(method_name, k_result_is_ok_fn) == 0) { + lua_pushcfunction(state, _lua_result_is_ok_handler); + guard.increment(); + return 1; + } else if (strcmp(method_name, k_result_is_err_fn) == 0) { + lua_pushcfunction(state, _lua_result_is_err_handler); + guard.increment(); + return 1; + } else if (strcmp(method_name, k_result_unwrap_fn) == 0) { + lua_pushcfunction(state, _lua_result_unwrap_handler); + guard.increment(); + return 1; + } else if (strcmp(method_name, k_result_unwrap_err_fn) == 0) { + lua_pushcfunction(state, _lua_result_unwrap_err_handler); + guard.increment(); + return 1; + } else if (strcmp(method_name, k_result_expect_fn) == 0) { + lua_pushcfunction(state, _lua_result_expect_handler); + guard.increment(); + return 1; + } else if (strcmp(method_name, k_result_expect_err_fn) == 0) { + lua_pushcfunction(state, _lua_result_expect_err_handler); + guard.increment(); + return 1; + } else { + auto err_count = luaL_error(state, + "Index '%s' does not exist in result type (make sure to unwrap the result before using it)", + method_name); + guard.increment(err_count); + return err_count; + } + } + static void _push_vector_vals(lua_State *state, const ObjectType &element_type, const ArrayBlob &vec) { argus_assert(vec.size() < INT_MAX); for (size_t i = 0; i < vec.size(); i++) { @@ -782,6 +954,30 @@ namespace argus { } } + static void _push_result(lua_State *state, const ResultWrapper &result) { + // create userdata to return + ResultWrapper *udata = reinterpret_cast( + lua_newuserdata(state, sizeof(ResultWrapper) + result.get_size())); + new(udata) ResultWrapper(result); + + // create metatable + luaL_newmetatable(state, k_mt_result); + + // indexing is handled by a delgating function instead of a dispatch + // table so that we can display a nice error message when a script tries + // to use a result directly + + // push index handler function + lua_pushcfunction(state, _lua_result_index_handler); + // bind handler to metatable + lua_setfield(state, -2, k_lua_index); + + // bind metatable to userdata + lua_setmetatable(state, -2); + + // table is now on top of stack + } + static void _push_value(lua_State *state, const ObjectWrapper &wrapper) { argus_assert(wrapper.type.type != IntegralType::Void); @@ -797,8 +993,7 @@ namespace argus { lua_pushboolean(state, _unwrap_boolean_wrapper(wrapper)); break; case IntegralType::String: - lua_pushstring(state, reinterpret_cast( - wrapper.is_on_heap ? wrapper.heap_ptr : wrapper.value)); + lua_pushstring(state, reinterpret_cast(wrapper.get_ptr0())); break; case IntegralType::Struct: { argus_assert(wrapper.type.type_name.has_value()); @@ -812,8 +1007,8 @@ namespace argus { break; } case IntegralType::Pointer: { - argus_assert(wrapper.type.type_name.has_value()); argus_assert(wrapper.type.type_index.has_value()); + argus_assert(wrapper.type.type_name.has_value()); void *ptr = *reinterpret_cast(wrapper.get_ptr0()); @@ -831,14 +1026,14 @@ namespace argus { break; } case IntegralType::Vector: { - auto &vec = *reinterpret_cast(wrapper.is_on_heap ? wrapper.heap_ptr : wrapper.value); + auto &vec = wrapper.get_value(); affirm_precond(vec.size() <= INT_MAX, "Vector is too big"); // create table to return lua_createtable(state, int(vec.size()), 0); - argus_assert(wrapper.type.element_type.has_value()); - _push_vector_vals(state, *wrapper.type.element_type.value(), vec); + argus_assert(wrapper.type.primary_type.has_value()); + _push_vector_vals(state, *wrapper.type.primary_type.value(), vec); // create metatable luaL_newmetatable(state, k_mt_vector); @@ -852,8 +1047,7 @@ namespace argus { break; } case IntegralType::VectorRef: { - auto &vec = *reinterpret_cast( - wrapper.is_on_heap ? wrapper.heap_ptr : wrapper.value); + auto &vec = *reinterpret_cast(wrapper.get_ptr0()); // create userdata to return VectorWrapper *udata = reinterpret_cast(lua_newuserdata(state, sizeof(VectorWrapper))); @@ -877,6 +1071,13 @@ namespace argus { // table is now on top of stack break; } + case IntegralType::Result: { + const auto &result = wrapper.get_value(); + + _push_result(state, result); + + break; + } default: argus_assert(false); } diff --git a/engine/static/wm/include/argus/wm/display.hpp b/engine/static/wm/include/argus/wm/display.hpp index 01c094c2..7db817f7 100644 --- a/engine/static/wm/include/argus/wm/display.hpp +++ b/engine/static/wm/include/argus/wm/display.hpp @@ -19,8 +19,7 @@ #pragma once #include "argus/lowlevel/math.hpp" - -#include "argus/scripting.hpp" +#include "argus/lowlevel/misc.hpp" #include #include diff --git a/engine/static/wm/include/argus/wm/window.hpp b/engine/static/wm/include/argus/wm/window.hpp index b49fb4c8..0e2f66bf 100644 --- a/engine/static/wm/include/argus/wm/window.hpp +++ b/engine/static/wm/include/argus/wm/window.hpp @@ -20,10 +20,9 @@ #include "argus/lowlevel/atomic.hpp" #include "argus/lowlevel/math.hpp" +#include "argus/lowlevel/misc.hpp" #include "argus/lowlevel/time.hpp" -#include "argus/scripting.hpp" - #include "argus/wm/display.hpp" #include diff --git a/engine/static/wm/include/argus/wm/window_event.hpp b/engine/static/wm/include/argus/wm/window_event.hpp index 1ac1d1c6..3729f38c 100644 --- a/engine/static/wm/include/argus/wm/window_event.hpp +++ b/engine/static/wm/include/argus/wm/window_event.hpp @@ -19,10 +19,9 @@ #pragma once #include "argus/lowlevel/math.hpp" +#include "argus/lowlevel/misc.hpp" #include "argus/lowlevel/time.hpp" -#include "argus/scripting.hpp" - #include "argus/core/event.hpp" #include diff --git a/engine/static/wm/src/api_util.cpp b/engine/static/wm/src/api_util.cpp index efe5ccab..9e7780f3 100644 --- a/engine/static/wm/src/api_util.cpp +++ b/engine/static/wm/src/api_util.cpp @@ -18,6 +18,8 @@ #include "argus/lowlevel/enum_ops.hpp" +#include "argus/core/engine.hpp" + #include "argus/wm/api_util.hpp" #include "argus/wm/window.hpp" #include "internal/wm/pimpl/window.hpp"