From 99f064b3f41430f4f41e7f2e14ab2fe4b7c36353 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 27 Nov 2024 23:27:33 +0100 Subject: [PATCH 01/42] lua: Add global() functions to set/get global variables --- include/ppplugin/lua/lua_script.h | 18 ++++++++++++++++++ include/ppplugin/lua/plugin.h | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/ppplugin/lua/lua_script.h b/include/ppplugin/lua/lua_script.h index 5a456b1..e810156 100644 --- a/include/ppplugin/lua/lua_script.h +++ b/include/ppplugin/lua/lua_script.h @@ -39,6 +39,24 @@ class LuaScript { return { CallError::Code::symbolNotFound }; } + template + CallResult global(const std::string& variable_name) + { + if (state_.pushGlobal(variable_name)) { + if (auto value = state_.pop()) { + return value.value(); + } + return { CallError::Code::unknown }; + } + return { CallError::Code::symbolNotFound }; + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + state_.push(new_value); + state_.markGlobal(variable_name); + } + private: LuaScript(); diff --git a/include/ppplugin/lua/plugin.h b/include/ppplugin/lua/plugin.h index 23bacba..9709057 100644 --- a/include/ppplugin/lua/plugin.h +++ b/include/ppplugin/lua/plugin.h @@ -47,6 +47,17 @@ class LuaPlugin { return script_.call(function_name, std::forward(args)...); } + template + CallResult global(const std::string& variable_name) + { + return script_.global(variable_name); + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + script_.global(variable_name, std::forward(new_value)); + } + private: explicit LuaPlugin(LuaScript&& script) : script_ { std::move(script) } From 3dcc4a897d5ee4259a537c711b9f37bc177137bf Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 27 Nov 2024 23:56:43 +0100 Subject: [PATCH 02/42] cpp: First draft for set/get global variables --- include/ppplugin/cpp/plugin.h | 18 +++++++++++++ include/ppplugin/detail/boost_dll_loader.h | 1 + src/boost_dll_loader.cpp | 30 ++++++++++++++-------- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/include/ppplugin/cpp/plugin.h b/include/ppplugin/cpp/plugin.h index e06eddd..14423ce 100644 --- a/include/ppplugin/cpp/plugin.h +++ b/include/ppplugin/cpp/plugin.h @@ -42,6 +42,24 @@ class CppPlugin { plugin_, function_name, std::forward(args)...); } + template + CallResult global(const std::string& variable_name) + { + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + return *reinterpret_cast(p.value().value()); + } + return { p.error().value() }; + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + *reinterpret_cast(p.value().value()) = std::forward(new_value); + } + } + private: CppPlugin() = default; diff --git a/include/ppplugin/detail/boost_dll_loader.h b/include/ppplugin/detail/boost_dll_loader.h index 7bb7b42..2e85ce3 100644 --- a/include/ppplugin/detail/boost_dll_loader.h +++ b/include/ppplugin/detail/boost_dll_loader.h @@ -20,6 +20,7 @@ namespace ppplugin::detail::boost_dll { [[nodiscard]] CallResult getFunctionSymbol(const boost::dll::shared_library& library, const std::string& function_name); [[nodiscard]] CallResult getFunctionPointerSymbol(const boost::dll::shared_library& library, const std::string& function_name); +[[nodiscard]] CallResult getSymbol(const boost::dll::shared_library& library, const std::string& function_name); template [[nodiscard]] CallResult call(const boost::dll::shared_library& library, const std::string& function_name, Args&&... args) diff --git a/src/boost_dll_loader.cpp b/src/boost_dll_loader.cpp index 3921253..b89fe0e 100644 --- a/src/boost_dll_loader.cpp +++ b/src/boost_dll_loader.cpp @@ -10,7 +10,7 @@ #include namespace { -std::optional isCallable(const boost::dll::shared_library& library, const std::string& function_name) +[[nodiscard]] std::optional isCallable(const boost::dll::shared_library& library, const std::string& function_name) { if (!library.is_loaded()) { return ppplugin::CallError::Code::notLoaded; @@ -25,7 +25,7 @@ std::optional isCallable(const boost::dll::shared_library& } // namespace namespace ppplugin::detail::boost_dll { -[[nodiscard]] CallResult getFunctionSymbol(const boost::dll::shared_library& library, const std::string& function_name) +CallResult getFunctionSymbol(const boost::dll::shared_library& library, const std::string& function_name) { if (auto error = isCallable(library, function_name)) { return { *error }; @@ -41,7 +41,7 @@ namespace ppplugin::detail::boost_dll { return { CallError::Code::unknown }; } -[[nodiscard]] CallResult getFunctionPointerSymbol(const boost::dll::shared_library& library, const std::string& function_name) +CallResult getFunctionPointerSymbol(const boost::dll::shared_library& library, const std::string& function_name) { if (auto error = isCallable(library, function_name)) { return { *error }; @@ -57,13 +57,23 @@ namespace ppplugin::detail::boost_dll { return { CallError::Code::unknown }; } -/** - * Attempt to load shared library from given path. - * - * Returned library object is not necessarily loaded, check - * boost::dll::shared_library::is_loaded(). - */ -[[nodiscard]] std::optional loadSharedLibrary( +CallResult getSymbol(const boost::dll::shared_library& library, const std::string& function_name) +{ + if (auto error = isCallable(library, function_name)) { + return { *error }; + } + try { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + if (auto** symbol = &library.get(function_name)) { + return symbol; + } + } catch (const std::exception& exception) { + return CallError { CallError::Code::unknown, exception.what() }; + } + return { CallError::Code::unknown }; +} + +std::optional loadSharedLibrary( const std::filesystem::path& plugin_library_path) { if (!std::filesystem::exists(plugin_library_path)) { From 126fee53c5f7d80632ef0f6455551a289bdcea7a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 27 Nov 2024 23:59:17 +0100 Subject: [PATCH 03/42] c: Add temporary version for global() --- include/ppplugin/c/plugin.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/ppplugin/c/plugin.h b/include/ppplugin/c/plugin.h index faf1c07..fc34def 100644 --- a/include/ppplugin/c/plugin.h +++ b/include/ppplugin/c/plugin.h @@ -31,6 +31,25 @@ class CPlugin { plugin_, function_name, std::forward(args)...); } + // TODO: remove code duplication here and in C++ plugin + template + CallResult global(const std::string& variable_name) + { + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + return *reinterpret_cast(p.value().value()); + } + return { p.error().value() }; + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + *reinterpret_cast(p.value().value()) = std::forward(new_value); + } + } + private: CPlugin() = default; From 8a537cb84a2cbc3dca5bf5fe33d793b31d99c63b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 27 Nov 2024 23:59:46 +0100 Subject: [PATCH 04/42] python: Add dummy global() functions --- include/ppplugin/python/plugin.h | 11 +++++++++++ include/ppplugin/python/python_interpreter.h | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/ppplugin/python/plugin.h b/include/ppplugin/python/plugin.h index bf9fb16..4737557 100644 --- a/include/ppplugin/python/plugin.h +++ b/include/ppplugin/python/plugin.h @@ -27,6 +27,17 @@ class PythonPlugin { template [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); + template + CallResult global(const std::string& variable_name) + { + return interpreter_.global(variable_name); + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + interpreter_.global(variable_name, std::forward(new_value)); + } + private: PythonPlugin() = default; diff --git a/include/ppplugin/python/python_interpreter.h b/include/ppplugin/python/python_interpreter.h index 592ff9d..692e682 100644 --- a/include/ppplugin/python/python_interpreter.h +++ b/include/ppplugin/python/python_interpreter.h @@ -20,6 +20,22 @@ class PythonInterpreter { template CallResult call(const std::string& function_name, Args&&... args); + template + CallResult global(const std::string& variable_name) + { + // TODO implement global for Python + (void)variable_name; + throw std::logic_error { "unimplemented" }; + } + template + void global(const std::string& variable_name, VariableType&& new_value) + { + // TODO implement global for Python + (void)variable_name; + (void)new_value; + throw std::logic_error { "unimplemented" }; + } + private: PyThreadState* state() { return state_.get(); } PyObject* mainModule() { return main_module_.get(); } From bd7be126b817ad7cc371c6770108c9b41dd9b6af Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 28 Nov 2024 00:00:07 +0100 Subject: [PATCH 05/42] GenericPlugin: Add global() function --- include/ppplugin/plugin.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/ppplugin/plugin.h b/include/ppplugin/plugin.h index 05a6950..ff6e141 100644 --- a/include/ppplugin/plugin.h +++ b/include/ppplugin/plugin.h @@ -57,6 +57,12 @@ class GenericPlugin { template [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); + template + CallResult global(const std::string& variable_name); + // TODO: return error + template + void global(const std::string& variable_name, VariableType&& new_value); + template std::optional> plugin(); @@ -98,6 +104,28 @@ CallResult GenericPlugin::call( plugin_); } +template +template +CallResult GenericPlugin::global(const std::string& variable_name) +{ + return std::visit( + [&variable_name](auto& plugin) -> CallResult { + return plugin.template global(variable_name); + }, + plugin_); +} + +template +template +void GenericPlugin::global(const std::string& variable_name, VariableType&& new_value) +{ + std::visit( + [&variable_name, &new_value](auto& plugin) { + plugin.global(variable_name, std::forward(new_value)); + }, + plugin_); +} + template template std::optional> GenericPlugin::plugin() From b63a91697cf3516b8dab28d8ddc4064579c3f11c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 18 Feb 2025 22:37:38 +0100 Subject: [PATCH 06/42] NoopPlugin: Add global() implementation Since this plugin is supposed to do nothing, the global function will simply return a default constructed value for the getter and do nothing for the setter. --- include/ppplugin/noop_plugin.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/ppplugin/noop_plugin.h b/include/ppplugin/noop_plugin.h index 5a41d5c..778cd59 100644 --- a/include/ppplugin/noop_plugin.h +++ b/include/ppplugin/noop_plugin.h @@ -26,6 +26,16 @@ class NoopPlugin { } return CallResult {}; } + + template + CallResult global(const std::string& /*variable_name*/) + { + return VariableType {}; + } + template + void global(const std::string& /*variable_name*/, VariableType&& /*new_value*/) + { + } }; } // namespace ppplugin From 4154a2c93212c0f0a8ce77645d7bf938d07b9f20 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 00:36:39 +0100 Subject: [PATCH 07/42] templates: Add SecondTemplateParameterOr helper This will return the second template parameter of a template class. It will be used for retrieving the error of Expected when it is not clear that the class actually has enough template parameters. A default value can be passed in case the class does not have sufficient template parameters. --- include/ppplugin/detail/template_helpers.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/ppplugin/detail/template_helpers.h b/include/ppplugin/detail/template_helpers.h index f21c8f7..0074bd5 100644 --- a/include/ppplugin/detail/template_helpers.h +++ b/include/ppplugin/detail/template_helpers.h @@ -99,6 +99,23 @@ struct FirstOr { template using FirstOrT = typename FirstOr::Type; +/** + * Get second template parameter of given class. + * If the class does not have any or sufficient template parameters, + * return given DefaultType. + */ +template +struct SecondTemplateParameterOr { + using Type = DefaultType; +}; +template typename T, + typename U, typename V, typename... Ts> +struct SecondTemplateParameterOr> { + using Type = V; +}; +template +using SecondTemplateParameterOrT = typename SecondTemplateParameterOr::Type; + /** * Wrap types into given type "Wrapper" (which has to accept exactly one template argument, * but can have more defaulted template parameters) and puts them into the first given type @@ -170,7 +187,6 @@ struct IsSpecialization, ExpectedType> : std::true_type { }; template using IsStdTuple = IsSpecialization; - } // namespace ppplugin::detail::templates #endif // PPPLUGIN_DETAIL_TEMPLATE_HELPERS_H From 7541a191ccfdc42e40e16fb7defa512e077cdfe8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 00:45:21 +0100 Subject: [PATCH 08/42] CallError|LuaState: Add incorrectType error code Although the class is called CallError, it is also used for errors when setting/getting variables. Thus, the new error class incorrectType can help narrowing down the actual issue when passing the wrong type for a variable. This can only be detected in the scripting languages. --- include/ppplugin/errors.h | 1 + include/ppplugin/lua/lua_script.h | 2 +- src/lua_state.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/ppplugin/errors.h b/include/ppplugin/errors.h index c23d4e1..c1d87cb 100644 --- a/include/ppplugin/errors.h +++ b/include/ppplugin/errors.h @@ -16,6 +16,7 @@ class CallError { unknown, notLoaded, symbolNotFound, + incorrectType, }; // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) diff --git a/include/ppplugin/lua/lua_script.h b/include/ppplugin/lua/lua_script.h index e810156..d6b8669 100644 --- a/include/ppplugin/lua/lua_script.h +++ b/include/ppplugin/lua/lua_script.h @@ -46,7 +46,7 @@ class LuaScript { if (auto value = state_.pop()) { return value.value(); } - return { CallError::Code::unknown }; + return { CallError::Code::incorrectType }; } return { CallError::Code::symbolNotFound }; } diff --git a/src/lua_state.cpp b/src/lua_state.cpp index b2be374..c79aa25 100644 --- a/src/lua_state.cpp +++ b/src/lua_state.cpp @@ -23,7 +23,7 @@ LuaState::LuaState() } LuaState::LuaState(lua_State* state) - : state_ { state, [](lua_State*) {} } + : state_ { state, [](lua_State*) { } } { } From 24a88ea51bad1af8720c0057d0c174cb85bfeb63 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:22:15 +0100 Subject: [PATCH 09/42] templates: Add C++17 compatible helper for std::remove_cvref_t This alternative is called RemoveCvrefT. --- include/ppplugin/detail/template_helpers.h | 6 ++++++ include/ppplugin/expected.h | 21 +++++++++++++++++---- include/ppplugin/lua/lua_state.h | 3 ++- include/ppplugin/plugin.h | 5 ++--- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/ppplugin/detail/template_helpers.h b/include/ppplugin/detail/template_helpers.h index 0074bd5..d8efbf3 100644 --- a/include/ppplugin/detail/template_helpers.h +++ b/include/ppplugin/detail/template_helpers.h @@ -187,6 +187,12 @@ struct IsSpecialization, ExpectedType> : std::true_type { }; template using IsStdTuple = IsSpecialization; + +/** + * C++17 compatible version of std::remove_cvref_t. + */ +template +using RemoveCvrefT = std::remove_cv_t>; } // namespace ppplugin::detail::templates #endif // PPPLUGIN_DETAIL_TEMPLATE_HELPERS_H diff --git a/include/ppplugin/expected.h b/include/ppplugin/expected.h index 2fbf6ca..36a4ca4 100644 --- a/include/ppplugin/expected.h +++ b/include/ppplugin/expected.h @@ -1,6 +1,8 @@ #ifndef PPPLUGIN_EXPECTED_H #define PPPLUGIN_EXPECTED_H +#include "detail/template_helpers.h" + #include #include #include @@ -112,6 +114,17 @@ class Expected { template friend class Expected; + // andThen wraps the result of the function into an Expected, except if the function + // returns an Expected which has an error matching the error type of the current instance. + template + using AndThenReturnType = std::conditional_t< + detail::templates::IsSpecialization, Expected>::value, + std::conditional_t< + std::is_same_v>, E>, + InvokeResult, + Expected>, + Expected>; + // actually, these functions also have a check, but will throw on error [[nodiscard]] constexpr T& uncheckedValue() & { return std::get(value_); } [[nodiscard]] constexpr const T& uncheckedValue() const& { return std::get(value_); } @@ -446,13 +459,13 @@ template constexpr auto Expected::andThen(F&& func) const& { if constexpr (std::is_invocable_v) { - using ReturnType = Expected, E>; + using ReturnType = AndThenReturnType>; if (!hasValue()) { return static_cast(uncheckedError()); } return static_cast(std::forward(func)(uncheckedValue())); } else if constexpr (std::is_invocable_v) { - using ReturnType = Expected, E>; + using ReturnType = AndThenReturnType>; if (!hasValue()) { return static_cast(uncheckedError()); } @@ -467,7 +480,7 @@ template constexpr auto Expected::andThen(F&& func) && { if constexpr (std::is_invocable_v) { - using ReturnType = Expected, E>; + using ReturnType = AndThenReturnType>; if (!hasValue()) { return static_cast(std::move(*this).uncheckedError()); } @@ -479,7 +492,7 @@ constexpr auto Expected::andThen(F&& func) && std::move(*this).uncheckedValue())); } } else if constexpr (std::is_invocable_v) { - using ReturnType = Expected, E>; + using ReturnType = AndThenReturnType>; if (!hasValue()) { return static_cast(std::move(*this).uncheckedError()); } diff --git a/include/ppplugin/lua/lua_state.h b/include/ppplugin/lua/lua_state.h index 17c8555..1540384 100644 --- a/include/ppplugin/lua/lua_state.h +++ b/include/ppplugin/lua/lua_state.h @@ -4,6 +4,7 @@ #include "lua_helpers.h" #include "ppplugin/detail/compatibility_utils.h" #include "ppplugin/detail/function_details.h" +#include "ppplugin/detail/template_helpers.h" #include "ppplugin/errors.h" #include @@ -200,7 +201,7 @@ auto LuaState::pop(bool always_pop) template , bool>> std::optional LuaState::top() { - using PlainT = std::remove_cv_t>; + using PlainT = detail::templates::RemoveCvrefT; if constexpr (std::is_same_v) { return topDouble(); } else if constexpr (std::is_same_v) { diff --git a/include/ppplugin/plugin.h b/include/ppplugin/plugin.h index ff6e141..2e5aa4d 100644 --- a/include/ppplugin/plugin.h +++ b/include/ppplugin/plugin.h @@ -15,7 +15,6 @@ #ifndef PPPLUGIN_CPP17_COMPATIBILITY #include #endif // PPPLUGIN_CPP17_COMPATIBILITY -#include #include #include #include @@ -42,7 +41,7 @@ class GenericPlugin { GenericPlugin() = default; template >> || ...), bool> = true> + std::enable_if_t<(std::is_base_of_v> || ...), bool> = true> GenericPlugin(P&& plugin); // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) ~GenericPlugin() = default; @@ -74,7 +73,7 @@ using Plugin = GenericPlugin template >> || ...), bool>> + std::enable_if_t<(std::is_base_of_v> || ...), bool>> // NOLINTNEXTLINE(bugprone-forwarding-reference-overload); enable_if condition prevents hiding copy/move ctors GenericPlugin::GenericPlugin(P&& plugin) : plugin_(std::forward

(plugin)) From a3c64ece2c87b062e3436e7c1906558c3c9d18c8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:24:36 +0100 Subject: [PATCH 10/42] python: Move C++ type to PyObject conversion to PythonObject Previously, this was performed in PythonTuple. However, this is less flexible since an unnecessary PythonTuple needs to be constructed that way which is why it was moved to PythonObject. --- include/ppplugin/python/python_object.h | 26 ++++++++- include/ppplugin/python/python_tuple.h | 24 +------- src/python_object.cpp | 66 ++++++++++++++++++++- src/python_tuple.cpp | 76 +------------------------ 4 files changed, 94 insertions(+), 98 deletions(-) diff --git a/include/ppplugin/python/python_object.h b/include/ppplugin/python/python_object.h index c8a06ed..e3dafac 100644 --- a/include/ppplugin/python/python_object.h +++ b/include/ppplugin/python/python_object.h @@ -13,10 +13,28 @@ class PythonObject { PythonObject(); explicit PythonObject(PyObject* object); + // NOLINTBEGIN(bugprone-easily-swappable-parameters) + // NOLINTBEGIN(google-runtime-int) + static PythonObject from(double value); + static PythonObject from(unsigned int value); + static PythonObject from(int value); + static PythonObject from(unsigned long value); + static PythonObject from(long value); + static PythonObject from(unsigned long long value); + static PythonObject from(long long value); + static PythonObject from(const char* value); + static PythonObject from(std::string_view value); + static PythonObject from(const std::string& value); + static PythonObject from(bool value); + static PythonObject from(std::nullptr_t); + // NOLINTEND(google-runtime-int) + // NOLINTEND(bugprone-easily-swappable-parameters) + // TODO: also make adding function (via function pointer) possible? + /** * Wrap given PyObject into PythonObject without claiming * ownership (no Py_DECREF at end of lifetime). - * Use this to access member functions for non-owning PyObjects. + * Use this to access member functions for non-owned PyObjects. */ static PythonObject wrap(PyObject* object); @@ -27,9 +45,15 @@ class PythonObject { return object() != nullptr; } + /** + * Get current value as given type. + */ template std::optional as(); + /** + * Cast current value to given type. + */ template std::optional to(); diff --git a/include/ppplugin/python/python_tuple.h b/include/ppplugin/python/python_tuple.h index a31837c..a109df8 100644 --- a/include/ppplugin/python/python_tuple.h +++ b/include/ppplugin/python/python_tuple.h @@ -4,10 +4,6 @@ #include "python_forward_defs.h" #include "python_object.h" -#include -#include -#include - namespace ppplugin { class PythonTuple { public: @@ -24,23 +20,7 @@ class PythonTuple { template void fillTuple(int start_index, T&& arg, Args&&... args); - // NOLINTBEGIN(bugprone-easily-swappable-parameters) - // NOLINTBEGIN(google-runtime-int) - void setTupleItem(int index, double value); - void setTupleItem(int index, unsigned int value); - void setTupleItem(int index, int value); - void setTupleItem(int index, unsigned long value); - void setTupleItem(int index, long value); - void setTupleItem(int index, unsigned long long value); - void setTupleItem(int index, long long value); - void setTupleItem(int index, const char* value); - void setTupleItem(int index, std::string_view value); - void setTupleItem(int index, const std::string& value); - void setTupleItem(int index, bool value); - void setTupleItem(int index, std::nullptr_t); - // NOLINTEND(google-runtime-int) - // NOLINTEND(bugprone-easily-swappable-parameters) - // TODO: also make adding function (via function pointer) possible? + void setTupleItem(int index, PythonObject value); private: PythonObject object_; @@ -58,7 +38,7 @@ PythonTuple::PythonTuple(Args&&... args) template void PythonTuple::fillTuple(int start_index, T&& arg, Args&&... args) { - setTupleItem(start_index, std::forward(arg)); + setTupleItem(start_index, PythonObject::from(std::forward(arg))); if constexpr (sizeof...(Args) > 0) { fillTuple(start_index + 1, std::forward(args)...); } diff --git a/src/python_object.cpp b/src/python_object.cpp index a858fd8..ccb6ac8 100644 --- a/src/python_object.cpp +++ b/src/python_object.cpp @@ -30,9 +30,73 @@ PythonObject::PythonObject(PyObject* object) PythonObject PythonObject::wrap(PyObject* object) { PythonObject new_object; - new_object.object_ = { object, [](auto*) {} }; + new_object.object_ = { object, [](auto*) { } }; return new_object; } +// NOLINTBEGIN(bugprone-easily-swappable-parameters) +// NOLINTBEGIN(google-runtime-int) +PythonObject PythonObject::from(double value) +{ + return PythonObject { PyFloat_FromDouble(value) }; +} + +PythonObject PythonObject::from(unsigned int value) +{ + return PythonObject::from(static_cast(value)); +} + +PythonObject PythonObject::from(int value) +{ + return PythonObject::from(static_cast(value)); +} + +PythonObject PythonObject::from(unsigned long value) +{ + return PythonObject { PyLong_FromUnsignedLong(value) }; +} + +PythonObject PythonObject::from(long value) +{ + return PythonObject { PyLong_FromLong(value) }; +} + +PythonObject PythonObject::from(unsigned long long value) +{ + return PythonObject { PyLong_FromUnsignedLongLong(value) }; +} + +PythonObject PythonObject::from(long long value) +{ + return PythonObject { PyLong_FromLongLong(value) }; +} +// NOLINTEND(google-runtime-int) +// NOLINTEND(bugprone-easily-swappable-parameters) + +PythonObject PythonObject::from(const char* value) +{ + return PythonObject { PyUnicode_FromString(value) }; +} + +PythonObject PythonObject::from(std::string_view value) +{ + return PythonObject { PyUnicode_FromStringAndSize( + value.data(), static_cast(value.size())) }; +} + +PythonObject PythonObject::from(const std::string& value) +{ + return PythonObject::from(std::string_view { value }); +} + +PythonObject PythonObject::from(bool value) +{ + return PythonObject { PyBool_FromLong(static_cast(value)) }; +} + +PythonObject PythonObject::from(std::nullptr_t) +{ + return PythonObject::wrap(Py_None); +} std::optional PythonObject::asInt() { diff --git a/src/python_tuple.cpp b/src/python_tuple.cpp index f8114a7..df9b48a 100644 --- a/src/python_tuple.cpp +++ b/src/python_tuple.cpp @@ -31,82 +31,10 @@ PyObject* PythonTuple::initTuple(int size) // NOLINTBEGIN(bugprone-easily-swappable-parameters) // NOLINTBEGIN(google-runtime-int) -void PythonTuple::setTupleItem(int index, double value) +void PythonTuple::setTupleItem(int index, PythonObject value) { - auto* py_value = PyFloat_FromDouble(value); + auto* py_value = value.pyObject(); assert(py_value); assert(PyTuple_SetItem(object(), index, py_value) == 0); } - -void PythonTuple::setTupleItem(int index, unsigned int value) -{ - setTupleItem(index, static_cast(value)); -} - -void PythonTuple::setTupleItem(int index, int value) -{ - setTupleItem(index, static_cast(value)); -} - -void PythonTuple::setTupleItem(int index, unsigned long value) -{ - auto* py_value = PyLong_FromUnsignedLong(value); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, long value) -{ - auto* py_value = PyLong_FromLong(value); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, unsigned long long value) -{ - auto* py_value = PyLong_FromUnsignedLongLong(value); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, long long value) -{ - auto* py_value = PyLong_FromLongLong(value); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} -// NOLINTEND(google-runtime-int) -// NOLINTEND(bugprone-easily-swappable-parameters) - -void PythonTuple::setTupleItem(int index, const char* value) -{ - auto* py_value = PyUnicode_FromString(value); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, std::string_view value) -{ - auto* py_value = PyUnicode_FromStringAndSize( - value.data(), static_cast(value.size())); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, const std::string& value) -{ - setTupleItem(index, std::string_view { value }); -} - -void PythonTuple::setTupleItem(int index, bool value) -{ - auto* py_value = PyBool_FromLong(static_cast(value)); - assert(py_value); - assert(PyTuple_SetItem(object(), index, py_value) == 0); -} - -void PythonTuple::setTupleItem(int index, std::nullptr_t) -{ - assert(PyTuple_SetItem(object(), index, Py_None) == 0); -} } // namespace ppplugin From 61467569d361e7fda46e1653dd45902d3ee95cb1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:26:17 +0100 Subject: [PATCH 11/42] python: Implement global() setter and getter --- include/ppplugin/python/plugin.h | 22 +++++++----- include/ppplugin/python/python_interpreter.h | 37 +++++++++++++------- src/python_interpreter.cpp | 28 +++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/include/ppplugin/python/plugin.h b/include/ppplugin/python/plugin.h index 4737557..2d102c2 100644 --- a/include/ppplugin/python/plugin.h +++ b/include/ppplugin/python/plugin.h @@ -1,7 +1,6 @@ #ifndef PPPLUGIN_PYTHON_PLUGIN_H #define PPPLUGIN_PYTHON_PLUGIN_H -#include "ppplugin/detail/function_details.h" #include "ppplugin/errors.h" #include "ppplugin/expected.h" #include "ppplugin/python/python_interpreter.h" @@ -28,15 +27,9 @@ class PythonPlugin { [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name) - { - return interpreter_.global(variable_name); - } + CallResult global(const std::string& variable_name); template - void global(const std::string& variable_name, VariableType&& new_value) - { - interpreter_.global(variable_name, std::forward(new_value)); - } + void global(const std::string& variable_name, VariableType&& new_value); private: PythonPlugin() = default; @@ -50,6 +43,17 @@ CallResult PythonPlugin::call(const std::string& function_name, Arg { return interpreter_.call(function_name, std::forward(args)...); } + +template +CallResult PythonPlugin::global(const std::string& variable_name) +{ + return interpreter_.global(variable_name); +} +template +void PythonPlugin::global(const std::string& variable_name, VariableType&& new_value) +{ + interpreter_.global(variable_name, std::forward(new_value)); +} } // namespace ppplugin #endif // PPPLUGIN_PYTHON_PLUGIN_H diff --git a/include/ppplugin/python/python_interpreter.h b/include/ppplugin/python/python_interpreter.h index 692e682..703c64d 100644 --- a/include/ppplugin/python/python_interpreter.h +++ b/include/ppplugin/python/python_interpreter.h @@ -21,20 +21,9 @@ class PythonInterpreter { CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name) - { - // TODO implement global for Python - (void)variable_name; - throw std::logic_error { "unimplemented" }; - } + CallResult global(const std::string& variable_name); template - void global(const std::string& variable_name, VariableType&& new_value) - { - // TODO implement global for Python - (void)variable_name; - (void)new_value; - throw std::logic_error { "unimplemented" }; - } + void global(const std::string& variable_name, VariableType&& new_value); private: PyThreadState* state() { return state_.get(); } @@ -42,6 +31,9 @@ class PythonInterpreter { CallResult internalCall(const std::string& function_name, PyObject* args); + CallResult internalGlobal(const std::string& variable_name); + void internalGlobal(const std::string& variable_name, PythonObject new_value); + private: std::unique_ptr> main_module_; std::unique_ptr state_; @@ -61,6 +53,25 @@ CallResult PythonInterpreter::call(const std::string& function_name } }); } + +template +CallResult PythonInterpreter::global(const std::string& variable_name) +{ + PythonGuard python_guard { state() }; + return internalGlobal(variable_name).andThen([](PythonObject&& object) -> CallResult { + if (auto result = object.template as()) { + return *result; + } + return { CallError::Code::incorrectType }; + }); +} + +template +void PythonInterpreter::global(const std::string& variable_name, VariableType&& new_value) +{ + PythonGuard python_guard { state() }; + internalGlobal(variable_name, PythonObject::from(new_value)); +} } // namespace ppplugin #endif // PPPLUGIN_PYTHON_INTERPRETER_H diff --git a/src/python_interpreter.cpp b/src/python_interpreter.cpp index c71a642..10130b9 100644 --- a/src/python_interpreter.cpp +++ b/src/python_interpreter.cpp @@ -175,4 +175,32 @@ CallResult PythonInterpreter::internalCall(const std::string& func return result; } +CallResult PythonInterpreter::internalGlobal(const std::string& variable_name) +{ + // TODO: find uniform way to manage guards (here: already locked in PythonPlugin::global) + // PythonGuard python_guard { state() }; + auto* variable = PyObject_GetAttrString(mainModule(), variable_name.c_str()); + if (variable == nullptr) { + if (PythonException::occurred()) { + if (auto exception = PythonException::latest()) { + return CallError { + CallError::Code::symbolNotFound, + exception->toString() + }; + } + return CallError { CallError::Code::symbolNotFound }; + } + return { CallError::Code::unknown }; + } + return PythonObject { variable }; +} + +void PythonInterpreter::internalGlobal(const std::string& variable_name, PythonObject new_value) +{ + // TODO: find uniform way to manage guards (here: already locked in PythonPlugin::global) + // PythonGuard python_guard { state() }; + PythonObject attribute_name { PyUnicode_FromString(variable_name.c_str()) }; + // TODO: return error on failure + [[maybe_unused]] auto result = PyObject_SetAttr(mainModule(), attribute_name.pyObject(), new_value.pyObject()); +} } // namespace ppplugin From 1f629719ba5f2edefd02c602dada9d19db914ab5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:27:06 +0100 Subject: [PATCH 12/42] python: Fix conversion with null bytes in bytes object Previously, the returned string was interpreted as a null-terminated C-string. However, a Python bytes object may contain null bytes in the string which is why the separately stored string length must be used to construct the correct string. --- src/python_object.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python_object.cpp b/src/python_object.cpp index ccb6ac8..91b7d77 100644 --- a/src/python_object.cpp +++ b/src/python_object.cpp @@ -147,11 +147,12 @@ std::optional PythonObject::asString() return result; } if (PyBytes_Check(object()) != 0) { - auto* result = PyBytes_AsString(object()); + const auto* result = PyBytes_AsString(object()); if (result == nullptr) { return std::nullopt; } - return std::string { result }; + auto length = PyBytes_Size(object()); + return std::string { result, 0, static_cast(length) }; } return std::nullopt; } From 3af5a12a05fb801bef60ec45bc2e3a902b8a36ef Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:36:36 +0100 Subject: [PATCH 13/42] cpp: Remove empty CppObjectPlugin This feature will probably not be implemented and thus the placeholder does no longer make sense. --- include/ppplugin/cpp/object_plugin.h | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 include/ppplugin/cpp/object_plugin.h diff --git a/include/ppplugin/cpp/object_plugin.h b/include/ppplugin/cpp/object_plugin.h deleted file mode 100644 index 6673b3a..0000000 --- a/include/ppplugin/cpp/object_plugin.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef PPPLUGIN_CPP_OBJECT_PLUGIN_H -#define PPPLUGIN_CPP_OBJECT_PLUGIN_H - -namespace ppplugin { -/** - * Plugin that has a creator function that creates an object - * of a known base class. - */ -template -class CppObjectPlugin { -public: - // TODO -}; -} // namespace ppplugin - -#endif // PPPLUGIN_CPP_OBJECT_PLUGIN_H From 703f04b8990fafc93eebabe1b443487917464ca1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Feb 2025 01:41:39 +0100 Subject: [PATCH 14/42] test: Add some Expected::andThen() tests --- test/expected_tests.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/expected_tests.cpp b/test/expected_tests.cpp index 40401dd..3af0f50 100644 --- a/test/expected_tests.cpp +++ b/test/expected_tests.cpp @@ -354,3 +354,45 @@ TEST(Expected, comparisonEqual) }, e1, e2); } + +TEST(Expected, andThenReturnRawValue) +{ + const Expected v { "test" }; + + auto result = v.andThen([]() { return 2.0; }); + + EXPECT_TRUE(result.hasValue()); + EXPECT_EQ(result.value().value_or(0.0), 2.0); +} + +TEST(Expected, andThenReturnValueInExpected) +{ + const Expected v { "test" }; + + auto result = v.andThen([]() -> Expected { return 3.0; }); + + EXPECT_TRUE(result.hasValue()); + EXPECT_EQ(result.value().value_or(0.0), 3.0); +} + +TEST(Expected, andThenReturnError) +{ + const Expected v { "test" }; + + auto result = v.andThen([]() -> decltype(v) { return 1; }); + + // static_assert(std::is_same_v); + + EXPECT_FALSE(result.hasValue()); + EXPECT_EQ(result.error().value_or(-1), 1); +} + +TEST(Expected, andThenError) +{ + const Expected e { 5 }; + + auto result = e.andThen([]() { return "abc"; }); + + EXPECT_FALSE(result.hasValue()); + EXPECT_EQ(result.error().value_or(-1), 5); +} From 11d054cb5026f6956b6f1cc63ab53fe90646bd3a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 14:09:54 +0200 Subject: [PATCH 15/42] src: Remove empty and unused ppplugin.cpp file --- src/CMakeLists.txt | 1 - src/ppplugin.cpp | 0 2 files changed, 1 deletion(-) delete mode 100644 src/ppplugin.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc5d063..e75c075 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ set(LIBRARY_SOURCES ppplugin.cpp) set(LIBRARY_SOURCES - "ppplugin.cpp" "boost_dll_loader.cpp" "lua_state.cpp" "lua_script.cpp" diff --git a/src/ppplugin.cpp b/src/ppplugin.cpp deleted file mode 100644 index e69de29..0000000 From dcadabd80c0e1f812b62b9f8309b4c2f86e3647f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 21:19:25 +0200 Subject: [PATCH 16/42] Plugin: Return error for setter of global variables This will allow to indicate to the user if setting the variable was successful. This can happen for C++ if the symbol does not exist or theoretically for Python where the API indicates that an exception can be thrown by the set function. --- include/ppplugin/c/plugin.h | 4 +++- include/ppplugin/cpp/plugin.h | 4 +++- include/ppplugin/lua/plugin.h | 5 ++++- include/ppplugin/noop_plugin.h | 3 ++- include/ppplugin/plugin.h | 7 +++---- include/ppplugin/python/plugin.h | 6 +++--- include/ppplugin/python/python_interpreter.h | 8 ++++---- src/python_interpreter.cpp | 13 ++++++++++--- 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/include/ppplugin/c/plugin.h b/include/ppplugin/c/plugin.h index fc34def..406f7db 100644 --- a/include/ppplugin/c/plugin.h +++ b/include/ppplugin/c/plugin.h @@ -42,12 +42,14 @@ class CPlugin { return { p.error().value() }; } template - void global(const std::string& variable_name, VariableType&& new_value) + CallResult global(const std::string& variable_name, VariableType&& new_value) { auto p = detail::boost_dll::getSymbol(plugin_, variable_name); if (p.hasValue()) { *reinterpret_cast(p.value().value()) = std::forward(new_value); + return {}; } + return { p.error().value() }; } private: diff --git a/include/ppplugin/cpp/plugin.h b/include/ppplugin/cpp/plugin.h index 14423ce..ab4788e 100644 --- a/include/ppplugin/cpp/plugin.h +++ b/include/ppplugin/cpp/plugin.h @@ -52,12 +52,14 @@ class CppPlugin { return { p.error().value() }; } template - void global(const std::string& variable_name, VariableType&& new_value) + CallResult global(const std::string& variable_name, VariableType&& new_value) { auto p = detail::boost_dll::getSymbol(plugin_, variable_name); if (p.hasValue()) { *reinterpret_cast(p.value().value()) = std::forward(new_value); + return {}; } + return { p.error().value() }; } private: diff --git a/include/ppplugin/lua/plugin.h b/include/ppplugin/lua/plugin.h index 9709057..47cc561 100644 --- a/include/ppplugin/lua/plugin.h +++ b/include/ppplugin/lua/plugin.h @@ -53,9 +53,12 @@ class LuaPlugin { return script_.global(variable_name); } template - void global(const std::string& variable_name, VariableType&& new_value) + CallResult global(const std::string& variable_name, VariableType&& new_value) { script_.global(variable_name, std::forward(new_value)); + // cannot fail in Lua; new value will be assigned regardless of previous type + // and variable will be created if it does not exist yet + return {}; } private: diff --git a/include/ppplugin/noop_plugin.h b/include/ppplugin/noop_plugin.h index 778cd59..f6b640e 100644 --- a/include/ppplugin/noop_plugin.h +++ b/include/ppplugin/noop_plugin.h @@ -33,8 +33,9 @@ class NoopPlugin { return VariableType {}; } template - void global(const std::string& /*variable_name*/, VariableType&& /*new_value*/) + CallResult global(const std::string& /*variable_name*/, VariableType&& /*new_value*/) { + return {}; } }; } // namespace ppplugin diff --git a/include/ppplugin/plugin.h b/include/ppplugin/plugin.h index 2e5aa4d..da21063 100644 --- a/include/ppplugin/plugin.h +++ b/include/ppplugin/plugin.h @@ -58,9 +58,8 @@ class GenericPlugin { template CallResult global(const std::string& variable_name); - // TODO: return error template - void global(const std::string& variable_name, VariableType&& new_value); + CallResult global(const std::string& variable_name, VariableType&& new_value); template std::optional> plugin(); @@ -116,9 +115,9 @@ CallResult GenericPlugin::global(const std::string& va template template -void GenericPlugin::global(const std::string& variable_name, VariableType&& new_value) +CallResult GenericPlugin::global(const std::string& variable_name, VariableType&& new_value) { - std::visit( + return std::visit( [&variable_name, &new_value](auto& plugin) { plugin.global(variable_name, std::forward(new_value)); }, diff --git a/include/ppplugin/python/plugin.h b/include/ppplugin/python/plugin.h index 2d102c2..e6e0a5b 100644 --- a/include/ppplugin/python/plugin.h +++ b/include/ppplugin/python/plugin.h @@ -29,7 +29,7 @@ class PythonPlugin { template CallResult global(const std::string& variable_name); template - void global(const std::string& variable_name, VariableType&& new_value); + CallResult global(const std::string& variable_name, VariableType&& new_value); private: PythonPlugin() = default; @@ -50,9 +50,9 @@ CallResult PythonPlugin::global(const std::string& variable_name) return interpreter_.global(variable_name); } template -void PythonPlugin::global(const std::string& variable_name, VariableType&& new_value) +CallResult PythonPlugin::global(const std::string& variable_name, VariableType&& new_value) { - interpreter_.global(variable_name, std::forward(new_value)); + return interpreter_.global(variable_name, std::forward(new_value)); } } // namespace ppplugin diff --git a/include/ppplugin/python/python_interpreter.h b/include/ppplugin/python/python_interpreter.h index 703c64d..48c8f76 100644 --- a/include/ppplugin/python/python_interpreter.h +++ b/include/ppplugin/python/python_interpreter.h @@ -23,7 +23,7 @@ class PythonInterpreter { template CallResult global(const std::string& variable_name); template - void global(const std::string& variable_name, VariableType&& new_value); + CallResult global(const std::string& variable_name, VariableType&& new_value); private: PyThreadState* state() { return state_.get(); } @@ -32,7 +32,7 @@ class PythonInterpreter { CallResult internalCall(const std::string& function_name, PyObject* args); CallResult internalGlobal(const std::string& variable_name); - void internalGlobal(const std::string& variable_name, PythonObject new_value); + CallResult internalGlobal(const std::string& variable_name, PythonObject new_value); private: std::unique_ptr> main_module_; @@ -67,10 +67,10 @@ CallResult PythonInterpreter::global(const std::string& variable_n } template -void PythonInterpreter::global(const std::string& variable_name, VariableType&& new_value) +CallResult PythonInterpreter::global(const std::string& variable_name, VariableType&& new_value) { PythonGuard python_guard { state() }; - internalGlobal(variable_name, PythonObject::from(new_value)); + return internalGlobal(variable_name, PythonObject::from(new_value)); } } // namespace ppplugin diff --git a/src/python_interpreter.cpp b/src/python_interpreter.cpp index 10130b9..3ceebe1 100644 --- a/src/python_interpreter.cpp +++ b/src/python_interpreter.cpp @@ -195,12 +195,19 @@ CallResult PythonInterpreter::internalGlobal(const std::string& va return PythonObject { variable }; } -void PythonInterpreter::internalGlobal(const std::string& variable_name, PythonObject new_value) +CallResult PythonInterpreter::internalGlobal(const std::string& variable_name, PythonObject new_value) { // TODO: find uniform way to manage guards (here: already locked in PythonPlugin::global) // PythonGuard python_guard { state() }; PythonObject attribute_name { PyUnicode_FromString(variable_name.c_str()) }; - // TODO: return error on failure - [[maybe_unused]] auto result = PyObject_SetAttr(mainModule(), attribute_name.pyObject(), new_value.pyObject()); + auto result = PyObject_SetAttr(mainModule(), attribute_name.pyObject(), new_value.pyObject()); + if (result < 0) { + auto exception = PythonException::latest(); + return CallError { + CallError::Code::unknown, + exception.value_or(PythonException {}).toString() + }; + } + return {}; } } // namespace ppplugin From fe94da9962d8e0b84b8af6f3c0c54b2bdd5b54d5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 22:32:45 +0200 Subject: [PATCH 17/42] everything: Move remaining member function definitions out of class definition Most member functions were already at the bottom of the header files or in their own source file. This commit moves the remaining definitions there as well. --- include/ppplugin/c/plugin.h | 81 ++++++++++++++-------------- include/ppplugin/cpp/plugin.h | 69 ++++++++++++------------ include/ppplugin/lua/lua_script.h | 89 ++++++++++++++++--------------- include/ppplugin/lua/plugin.h | 49 ++++++++--------- include/ppplugin/noop_plugin.h | 31 ++++++----- src/CMakeLists.txt | 3 ++ src/c_plugin.cpp | 18 +++++++ src/cpp_plugin.cpp | 18 +++++++ src/lua_plugin.cpp | 12 +++++ 9 files changed, 213 insertions(+), 157 deletions(-) create mode 100644 src/c_plugin.cpp create mode 100644 src/cpp_plugin.cpp create mode 100644 src/lua_plugin.cpp diff --git a/include/ppplugin/c/plugin.h b/include/ppplugin/c/plugin.h index 406f7db..94f8ac9 100644 --- a/include/ppplugin/c/plugin.h +++ b/include/ppplugin/c/plugin.h @@ -7,50 +7,18 @@ namespace ppplugin { class CPlugin { public: - static Expected load( - const std::filesystem::path& plugin_library_path) - { - if (auto shared_library = detail::boost_dll::loadSharedLibrary(plugin_library_path)) { - CPlugin new_plugin; - new_plugin.plugin_ = *shared_library; - return new_plugin; - } - return LoadError::unknown; - } + [[nodiscard]] static Expected load(const std::filesystem::path& plugin_library_path); + + // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) + operator bool() const; template - [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args) - { - static_assert(!std::is_reference_v, - "C does not support references for its return value!"); - static_assert(!(std::is_reference_v || ...), - "C does not support references for its arguments! " - "Consider passing the argument with an explicit cast to the desired type."); - - return detail::boost_dll::call( - plugin_, function_name, std::forward(args)...); - } + [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); - // TODO: remove code duplication here and in C++ plugin template - CallResult global(const std::string& variable_name) - { - auto p = detail::boost_dll::getSymbol(plugin_, variable_name); - if (p.hasValue()) { - return *reinterpret_cast(p.value().value()); - } - return { p.error().value() }; - } + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value) - { - auto p = detail::boost_dll::getSymbol(plugin_, variable_name); - if (p.hasValue()) { - *reinterpret_cast(p.value().value()) = std::forward(new_value); - return {}; - } - return { p.error().value() }; - } + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); private: CPlugin() = default; @@ -58,6 +26,41 @@ class CPlugin { private: boost::dll::shared_library plugin_; }; + +template +CallResult CPlugin::call(const std::string& function_name, Args&&... args) +{ + static_assert(!std::is_reference_v, + "C does not support references for its return value!"); + static_assert(!(std::is_reference_v || ...), + "C does not support references for its arguments! " + "Consider passing the argument with an explicit cast to the desired type."); + + return detail::boost_dll::call( + plugin_, function_name, std::forward(args)...); +} + +// TODO: remove code duplication here and in C++ plugin +template +CallResult CPlugin::global(const std::string& variable_name) +{ + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + return *reinterpret_cast(p.value().value()); + } + return { p.error().value() }; +} + +template +CallResult CPlugin::global(const std::string& variable_name, VariableType&& new_value) +{ + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + *reinterpret_cast(p.value().value()) = std::forward(new_value); + return {}; + } + return { p.error().value() }; +} } // namespace ppplugin #endif // PPPLUGIN_C_PLUGIN_H diff --git a/include/ppplugin/cpp/plugin.h b/include/ppplugin/cpp/plugin.h index ab4788e..867b3db 100644 --- a/include/ppplugin/cpp/plugin.h +++ b/include/ppplugin/cpp/plugin.h @@ -12,16 +12,7 @@ namespace ppplugin { class CppPlugin { public: - static Expected load( - const std::filesystem::path& plugin_library_path) - { - if (auto shared_library = detail::boost_dll::loadSharedLibrary(plugin_library_path)) { - CppPlugin new_plugin; - new_plugin.plugin_ = *shared_library; - return new_plugin; - } - return LoadError::unknown; - } + [[nodiscard]] static Expected load(const std::filesystem::path& plugin_library_path); ~CppPlugin() = default; CppPlugin(const CppPlugin&) = default; @@ -30,37 +21,15 @@ class CppPlugin { CppPlugin& operator=(CppPlugin&&) = default; // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions) - operator bool() const - { - return plugin_.is_loaded(); - } + operator bool() const; template - [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args) - { - return detail::boost_dll::call( - plugin_, function_name, std::forward(args)...); - } + [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name) - { - auto p = detail::boost_dll::getSymbol(plugin_, variable_name); - if (p.hasValue()) { - return *reinterpret_cast(p.value().value()); - } - return { p.error().value() }; - } + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value) - { - auto p = detail::boost_dll::getSymbol(plugin_, variable_name); - if (p.hasValue()) { - *reinterpret_cast(p.value().value()) = std::forward(new_value); - return {}; - } - return { p.error().value() }; - } + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); private: CppPlugin() = default; @@ -68,6 +37,34 @@ class CppPlugin { private: boost::dll::shared_library plugin_; }; + +template +CallResult CppPlugin::call(const std::string& function_name, Args&&... args) +{ + return detail::boost_dll::call( + plugin_, function_name, std::forward(args)...); +} + +template +CallResult CppPlugin::global(const std::string& variable_name) +{ + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + return *reinterpret_cast(p.value().value()); + } + return { p.error().value() }; +} + +template +CallResult CppPlugin::global(const std::string& variable_name, VariableType&& new_value) +{ + auto p = detail::boost_dll::getSymbol(plugin_, variable_name); + if (p.hasValue()) { + *reinterpret_cast(p.value().value()) = std::forward(new_value); + return {}; + } + return { p.error().value() }; +} } // namespace ppplugin #endif // PPPLUGIN_CPP_PLUGIN_H diff --git a/include/ppplugin/lua/lua_script.h b/include/ppplugin/lua/lua_script.h index d6b8669..5ceefe4 100644 --- a/include/ppplugin/lua/lua_script.h +++ b/include/ppplugin/lua/lua_script.h @@ -28,63 +28,26 @@ class LuaScript { LuaScript& operator=(LuaScript&&) noexcept = default; template - CallResult call(const std::string& function_name, Args&&... args) - { - if (state_.pushGlobal(function_name)) { - if (auto function = state_.top()) { - return (*function)(std::forward(args)...); - } - return CallError { CallError::Code::unknown, "Symbol does not match given type" }; - } - return { CallError::Code::symbolNotFound }; - } + [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name) - { - if (state_.pushGlobal(variable_name)) { - if (auto value = state_.pop()) { - return value.value(); - } - return { CallError::Code::incorrectType }; - } - return { CallError::Code::symbolNotFound }; - } + [[nodiscard]] CallResult global(const std::string& variable_name); template - void global(const std::string& variable_name, VariableType&& new_value) - { - state_.push(new_value); - state_.markGlobal(variable_name); - } + void global(const std::string& variable_name, VariableType&& new_value); private: LuaScript(); - bool run(); + [[nodiscard]] bool run(); - std::optional loadFile(const std::filesystem::path& lua_file, bool auto_run); + [[nodiscard]] std::optional loadFile(const std::filesystem::path& lua_file, bool auto_run); static void pcall(); template - void push(Args&&... args) - { - if constexpr (sizeof...(Args) > 0) { - state_.push(std::forward(args)...); - } - } + void push(Args&&... args); - /** - * Can be used to define global variables or functions for usage in Lua script. - */ - template - void setGlobal(const std::string& variable_name, T&& value) - { - push(std::forward(value)); - state_.markGlobal(variable_name); - } - - static std::string errorToString(int error_code); + [[nodiscard]] static std::string errorToString(int error_code); struct StackContext { // TODO: push error handling function to stack @@ -99,6 +62,44 @@ class LuaScript { LuaState state_; }; +template +CallResult LuaScript::call(const std::string& function_name, Args&&... args) +{ + if (state_.pushGlobal(function_name)) { + if (auto function = state_.top()) { + return (*function)(std::forward(args)...); + } + return CallError { CallError::Code::unknown, "Symbol does not match given type" }; + } + return { CallError::Code::symbolNotFound }; +} + +template +CallResult LuaScript::global(const std::string& variable_name) +{ + if (state_.pushGlobal(variable_name)) { + if (auto value = state_.pop()) { + return value.value(); + } + return { CallError::Code::incorrectType }; + } + return { CallError::Code::symbolNotFound }; +} +template +void LuaScript::global(const std::string& variable_name, VariableType&& new_value) +{ + push(std::forward(new_value)); + state_.markGlobal(variable_name); +} + +template +void LuaScript::push(Args&&... args) +{ + if constexpr (sizeof...(Args) > 0) { + state_.push(std::forward(args)...); + } +} + } // namespace ppplugin #endif // PPPLUGIN_LUA_SCRIPT_H diff --git a/include/ppplugin/lua/plugin.h b/include/ppplugin/lua/plugin.h index 47cc561..2cafd73 100644 --- a/include/ppplugin/lua/plugin.h +++ b/include/ppplugin/lua/plugin.h @@ -9,15 +9,8 @@ namespace ppplugin { class LuaPlugin { public: - static Expected load( - const std::filesystem::path& plugin_library_path, bool auto_run = true) - { - return LuaScript::load(plugin_library_path, auto_run) - .andThen([](auto script) { - LuaPlugin new_plugin { std::move(script) }; - return new_plugin; - }); - } + [[nodiscard]] static Expected load( + const std::filesystem::path& plugin_library_path, bool auto_run = true); ~LuaPlugin() = default; LuaPlugin(const LuaPlugin&) = delete; @@ -42,24 +35,12 @@ class LuaPlugin { * - std::tuple */ template - [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args) - { - return script_.call(function_name, std::forward(args)...); - } + [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name) - { - return script_.global(variable_name); - } + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value) - { - script_.global(variable_name, std::forward(new_value)); - // cannot fail in Lua; new value will be assigned regardless of previous type - // and variable will be created if it does not exist yet - return {}; - } + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); private: explicit LuaPlugin(LuaScript&& script) @@ -71,6 +52,26 @@ class LuaPlugin { LuaScript script_; }; +template +CallResult LuaPlugin::call(const std::string& function_name, Args&&... args) +{ + return script_.call(function_name, std::forward(args)...); +} + +template +CallResult LuaPlugin::global(const std::string& variable_name) +{ + return script_.global(variable_name); +} + +template +CallResult LuaPlugin::global(const std::string& variable_name, VariableType&& new_value) +{ + script_.global(variable_name, std::forward(new_value)); + // cannot fail in Lua; new value will be assigned regardless of previous type + // and variable will be created if it does not exist yet + return {}; +} } // namespace ppplugin #endif // PPPLUGIN_LUA_PLUGIN_H diff --git a/include/ppplugin/noop_plugin.h b/include/ppplugin/noop_plugin.h index f6b640e..5b0c240 100644 --- a/include/ppplugin/noop_plugin.h +++ b/include/ppplugin/noop_plugin.h @@ -14,30 +14,33 @@ class NoopPlugin { operator bool() const { return true; } template - CallResult call(std::string_view /*function_name*/, Args&&... /*args*/) - { - if constexpr (!std::is_void_v) { - if constexpr (std::is_default_constructible_v) { - return ReturnValue {}; - } else { - static_assert(!sizeof(ReturnValue), - "Return value of noop function must be default constructible!"); - } - } - return CallResult {}; - } + [[nodiscard]] CallResult call(std::string_view /*function_name*/, Args&&... /*args*/); template - CallResult global(const std::string& /*variable_name*/) + [[nodiscard]] CallResult global(const std::string& /*variable_name*/) { return VariableType {}; } template - CallResult global(const std::string& /*variable_name*/, VariableType&& /*new_value*/) + [[nodiscard]] CallResult global(const std::string& /*variable_name*/, VariableType&& /*new_value*/) { return {}; } }; + +template +CallResult NoopPlugin::call(std::string_view /*function_name*/, Args&&... /*args*/) +{ + if constexpr (!std::is_void_v) { + if constexpr (std::is_default_constructible_v) { + return ReturnValue {}; + } else { + static_assert(!sizeof(ReturnValue), + "Return value of noop function must be default constructible!"); + } + } + return CallResult {}; +} } // namespace ppplugin #endif // PPPLUGIN_NOOP_PLUGIN_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e75c075..eccba01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,9 @@ set(LIBRARY_SOURCES ppplugin.cpp) set(LIBRARY_SOURCES "boost_dll_loader.cpp" + "c_plugin.cpp" + "cpp_plugin.cpp" + "lua_plugin.cpp" "lua_state.cpp" "lua_script.cpp" "python_plugin.cpp" diff --git a/src/c_plugin.cpp b/src/c_plugin.cpp new file mode 100644 index 0000000..4a4fc24 --- /dev/null +++ b/src/c_plugin.cpp @@ -0,0 +1,18 @@ +#include "ppplugin/c/plugin.h" + +namespace ppplugin { +Expected CPlugin::load(const std::filesystem::path& plugin_library_path) +{ + if (auto shared_library = detail::boost_dll::loadSharedLibrary(plugin_library_path)) { + CPlugin new_plugin; + new_plugin.plugin_ = *shared_library; + return new_plugin; + } + return LoadError::unknown; +} + +CPlugin::operator bool() const +{ + return plugin_.is_loaded(); +} +} // namespace ppplugin diff --git a/src/cpp_plugin.cpp b/src/cpp_plugin.cpp new file mode 100644 index 0000000..c96c742 --- /dev/null +++ b/src/cpp_plugin.cpp @@ -0,0 +1,18 @@ +#include "ppplugin/cpp/plugin.h" + +namespace ppplugin { +Expected CppPlugin::load(const std::filesystem::path& plugin_library_path) +{ + if (auto shared_library = detail::boost_dll::loadSharedLibrary(plugin_library_path)) { + CppPlugin new_plugin; + new_plugin.plugin_ = *shared_library; + return new_plugin; + } + return LoadError::unknown; +} + +CppPlugin::operator bool() const +{ + return plugin_.is_loaded(); +} +} // namespace ppplugin diff --git a/src/lua_plugin.cpp b/src/lua_plugin.cpp new file mode 100644 index 0000000..24b9ff0 --- /dev/null +++ b/src/lua_plugin.cpp @@ -0,0 +1,12 @@ +#include "ppplugin/lua/plugin.h" + +namespace ppplugin { +Expected LuaPlugin::load(const std::filesystem::path& plugin_library_path, bool auto_run) +{ + return LuaScript::load(plugin_library_path, auto_run) + .andThen([](auto script) { + LuaPlugin new_plugin { std::move(script) }; + return new_plugin; + }); +} +} // namespace ppplugin From 0a14b9e4241a44dd845a0b8b392537c3ce9b5bae Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 22:37:24 +0200 Subject: [PATCH 18/42] python|lua|plugin: Add nodiscard modifier to member functions --- include/ppplugin/lua/lua_state.h | 28 ++++++------- include/ppplugin/plugin.h | 6 +-- include/ppplugin/plugin_manager.h | 8 ++-- include/ppplugin/python/plugin.h | 4 +- include/ppplugin/python/python_interpreter.h | 18 ++++---- include/ppplugin/python/python_object.h | 44 ++++++++++---------- include/ppplugin/python/python_tuple.h | 6 +-- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/include/ppplugin/lua/lua_state.h b/include/ppplugin/lua/lua_state.h index 1540384..2480d45 100644 --- a/include/ppplugin/lua/lua_state.h +++ b/include/ppplugin/lua/lua_state.h @@ -31,7 +31,7 @@ class LuaState { * Wrap given state in a LuaState and allow access to methods, * but do not claim ownership of the state. */ - static LuaState wrap(lua_State* state) { return LuaState(state); } + [[nodiscard]] static LuaState wrap(lua_State* state) { return LuaState(state); } [[nodiscard]] lua_State* state() { return state_.get(); } [[nodiscard]] const lua_State* state() const { return state_.get(); } @@ -55,22 +55,22 @@ class LuaState { * @return top-most stack value or std::nullopt if given type does not match */ template , bool> = true> - std::optional pop(bool always_pop = false); + [[nodiscard]] std::optional pop(bool always_pop = false); /** * Pop upmost element of type function from Lua stack. * * @return std::function wrapped in optional of given function type. */ template , bool> = true> - auto pop(bool always_pop = false); + [[nodiscard]] auto pop(bool always_pop = false); /** * Get top-most stack value. */ template , bool> = true> - std::optional top(); + [[nodiscard]] std::optional top(); template , bool> = true> - auto top(); + [[nodiscard]] auto top(); /** * Mark top-most stack value as global variable with given name. @@ -82,24 +82,24 @@ class LuaState { * * @return true if global of given name exists, otherwise false */ - bool pushGlobal(const std::string& variable_name); + [[nodiscard]] bool pushGlobal(const std::string& variable_name); /** * Check if top-most stack value is of type bool. */ - bool isBool(); + [[nodiscard]] bool isBool(); /** * Check if top-most stack value is of type number. */ - bool isNumber(); + [[nodiscard]] bool isNumber(); /** * Check if top-most stack value is of type string. */ - bool isString(); + [[nodiscard]] bool isString(); /** * Check if top-most stack value is of type function. */ - bool isFunction(); + [[nodiscard]] bool isFunction(); /** * Register function handler to be called in case of a Lua panic. @@ -118,13 +118,13 @@ class LuaState { [[nodiscard]] std::optional topInt(); [[nodiscard]] std::optional topDouble(); template - auto topFunction(); - const void* topPointer(); + [[nodiscard]] auto topFunction(); + [[nodiscard]] const void* topPointer(); /** * Check if top-most stack value is of type nil. */ - bool isNil(); + [[nodiscard]] bool isNil(); /** * Pop top-most value if it is a function. @@ -157,7 +157,7 @@ class LuaState { * * @return 0 on success, non-zero value on error */ - int pcall(std::size_t argument_count, std::size_t return_count); + [[nodiscard]] int pcall(std::size_t argument_count, std::size_t return_count); /** * Discard top-most stack value. diff --git a/include/ppplugin/plugin.h b/include/ppplugin/plugin.h index da21063..472868f 100644 --- a/include/ppplugin/plugin.h +++ b/include/ppplugin/plugin.h @@ -57,12 +57,12 @@ class GenericPlugin { [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name); + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value); + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); template - std::optional> plugin(); + [[nodiscard]] std::optional> plugin(); private: detail::templates::UniqueVariant plugin_; diff --git a/include/ppplugin/plugin_manager.h b/include/ppplugin/plugin_manager.h index 9fb84dc..a330807 100644 --- a/include/ppplugin/plugin_manager.h +++ b/include/ppplugin/plugin_manager.h @@ -36,7 +36,7 @@ class GenericPluginManager { /** * Load Lua script from given path. */ - Expected loadLuaPlugin( + [[nodiscard]] Expected loadLuaPlugin( const std::filesystem::path& plugin_library_path) { return LuaPlugin::load(plugin_library_path); @@ -45,7 +45,7 @@ class GenericPluginManager { /** * Load Python script from given path. */ - Expected loadPythonPlugin( + [[nodiscard]] Expected loadPythonPlugin( const std::filesystem::path& plugin_library_path) { return PythonPlugin::load(plugin_library_path); @@ -60,7 +60,7 @@ class GenericPluginManager { * created by the plugin are fully destroyed. * Failure to do so will result in a SEGFAULT. */ - Expected loadCppPlugin( + [[nodiscard]] Expected loadCppPlugin( const std::filesystem::path& plugin_library_path) { return CppPlugin::load(plugin_library_path); @@ -70,7 +70,7 @@ class GenericPluginManager { * This plugin can call any C function or C++ functions that were marked * as 'extern "C"'. */ - Expected loadCPlugin( + [[nodiscard]] Expected loadCPlugin( const std::filesystem::path& plugin_library_path) { return CPlugin::load(plugin_library_path); diff --git a/include/ppplugin/python/plugin.h b/include/ppplugin/python/plugin.h index e6e0a5b..a29b837 100644 --- a/include/ppplugin/python/plugin.h +++ b/include/ppplugin/python/plugin.h @@ -27,9 +27,9 @@ class PythonPlugin { [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name); + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value); + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); private: PythonPlugin() = default; diff --git a/include/ppplugin/python/python_interpreter.h b/include/ppplugin/python/python_interpreter.h index 48c8f76..51eaece 100644 --- a/include/ppplugin/python/python_interpreter.h +++ b/include/ppplugin/python/python_interpreter.h @@ -16,23 +16,23 @@ class PythonInterpreter { PythonInterpreter(); // TODO: call Py_Finalize() - std::optional load(const std::string& file_name); + [[nodiscard]] std::optional load(const std::string& file_name); template - CallResult call(const std::string& function_name, Args&&... args); + [[nodiscard]] CallResult call(const std::string& function_name, Args&&... args); template - CallResult global(const std::string& variable_name); + [[nodiscard]] CallResult global(const std::string& variable_name); template - CallResult global(const std::string& variable_name, VariableType&& new_value); + [[nodiscard]] CallResult global(const std::string& variable_name, VariableType&& new_value); private: - PyThreadState* state() { return state_.get(); } - PyObject* mainModule() { return main_module_.get(); } + [[nodiscard]] PyThreadState* state() { return state_.get(); } + [[nodiscard]] PyObject* mainModule() { return main_module_.get(); } - CallResult internalCall(const std::string& function_name, PyObject* args); + [[nodiscard]] CallResult internalCall(const std::string& function_name, PyObject* args); - CallResult internalGlobal(const std::string& variable_name); - CallResult internalGlobal(const std::string& variable_name, PythonObject new_value); + [[nodiscard]] CallResult internalGlobal(const std::string& variable_name); + [[nodiscard]] CallResult internalGlobal(const std::string& variable_name, PythonObject new_value); private: std::unique_ptr> main_module_; diff --git a/include/ppplugin/python/python_object.h b/include/ppplugin/python/python_object.h index e3dafac..ed00899 100644 --- a/include/ppplugin/python/python_object.h +++ b/include/ppplugin/python/python_object.h @@ -15,18 +15,18 @@ class PythonObject { // NOLINTBEGIN(bugprone-easily-swappable-parameters) // NOLINTBEGIN(google-runtime-int) - static PythonObject from(double value); - static PythonObject from(unsigned int value); - static PythonObject from(int value); - static PythonObject from(unsigned long value); - static PythonObject from(long value); - static PythonObject from(unsigned long long value); - static PythonObject from(long long value); - static PythonObject from(const char* value); - static PythonObject from(std::string_view value); - static PythonObject from(const std::string& value); - static PythonObject from(bool value); - static PythonObject from(std::nullptr_t); + [[nodiscard]] static PythonObject from(double value); + [[nodiscard]] static PythonObject from(unsigned int value); + [[nodiscard]] static PythonObject from(int value); + [[nodiscard]] static PythonObject from(unsigned long value); + [[nodiscard]] static PythonObject from(long value); + [[nodiscard]] static PythonObject from(unsigned long long value); + [[nodiscard]] static PythonObject from(long long value); + [[nodiscard]] static PythonObject from(const char* value); + [[nodiscard]] static PythonObject from(std::string_view value); + [[nodiscard]] static PythonObject from(const std::string& value); + [[nodiscard]] static PythonObject from(bool value); + [[nodiscard]] static PythonObject from(std::nullptr_t); // NOLINTEND(google-runtime-int) // NOLINTEND(bugprone-easily-swappable-parameters) // TODO: also make adding function (via function pointer) possible? @@ -36,9 +36,9 @@ class PythonObject { * ownership (no Py_DECREF at end of lifetime). * Use this to access member functions for non-owned PyObjects. */ - static PythonObject wrap(PyObject* object); + [[nodiscard]] static PythonObject wrap(PyObject* object); - PyObject* pyObject() { return object(); } + [[nodiscard]] PyObject* pyObject() { return object(); } explicit operator bool() { @@ -49,26 +49,26 @@ class PythonObject { * Get current value as given type. */ template - std::optional as(); + [[nodiscard]] std::optional as(); /** * Cast current value to given type. */ template - std::optional to(); + [[nodiscard]] std::optional to(); private: PyObject* object() { return object_.get(); } // TODO: use explicit template instantiations instead? - std::optional asInt(); + [[nodiscard]] std::optional asInt(); // NOLINTNEXTLINE(google-runtime-int) - std::optional asLongLong(); - std::optional asDouble(); - std::optional asBool(); - std::optional asString(); + [[nodiscard]] std::optional asLongLong(); + [[nodiscard]] std::optional asDouble(); + [[nodiscard]] std::optional asBool(); + [[nodiscard]] std::optional asString(); - std::optional toString(); + [[nodiscard]] std::optional toString(); private: std::unique_ptr object_; diff --git a/include/ppplugin/python/python_tuple.h b/include/ppplugin/python/python_tuple.h index a109df8..9bbcee6 100644 --- a/include/ppplugin/python/python_tuple.h +++ b/include/ppplugin/python/python_tuple.h @@ -10,12 +10,12 @@ class PythonTuple { template explicit PythonTuple(Args&&... args); - PyObject* pyObject() { return object(); } + [[nodiscard]] PyObject* pyObject() { return object(); } private: - PyObject* object() { return object_.pyObject(); } + [[nodiscard]] PyObject* object() { return object_.pyObject(); } - static PyObject* initTuple(int size); + [[nodiscard]] static PyObject* initTuple(int size); template void fillTuple(int start_index, T&& arg, Args&&... args); From 236842a26133f470bb2b5f5e0848ef96caa5d669 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 23:42:53 +0200 Subject: [PATCH 19/42] clang-tidy: Remove include-cleaner because of Python Since Python does have its own philosophy for header includes (always include Python.h as first header which also includes all other required headers), it is not feasible to use the include-cleaner in this project. Since there is no common pattern for the Python headers, it is also not possible to simply use the option "IgnoreHeaders" to disable this check only for Python headers. --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 932a5be..f600cd3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -6,7 +6,7 @@ Checks: > google-*,-google-readability-todo,-google-readability-braces-around-statements, hicpp-*,-hicpp-braces-around-statements,-hicpp-use-emplace,-hicpp-named-parameter, -hicpp-no-array-decay,-hicpp-uppercase-literal-suffix, - misc-*,-misc-no-recursion, + misc-*,-misc-include-cleaner,-misc-no-recursion, bugprone-*, boost-*, cert-*,-cert-dcl16-c,-cert-dcl51-cpp,-cert-dcl37-c, From fb5497779f758830564341448653c1569482e9a8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Mar 2025 23:46:37 +0200 Subject: [PATCH 20/42] ci: Update clang-tidy/clang-format and fix affected files --- .github/workflows/cpp-format.yml | 4 ++-- .github/workflows/cpp-lint.yml | 4 ++-- .../multi_language_plugin_manager.cpp | 12 ++++++------ src/boost_dll_loader.cpp | 3 +-- src/python_interpreter.cpp | 2 +- src/python_object.cpp | 10 ++-------- src/python_tuple.cpp | 2 -- test/scope_guard_tests.cpp | 4 ++-- 8 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.github/workflows/cpp-format.yml b/.github/workflows/cpp-format.yml index df59907..5fc5c62 100644 --- a/.github/workflows/cpp-format.yml +++ b/.github/workflows/cpp-format.yml @@ -28,9 +28,9 @@ jobs: - uses: actions/checkout@v3 - uses: jirutka/setup-alpine@v1 with: - branch: v3.19 + branch: v3.21 packages: > - clang-extra-tools + clang19-extra-tools # Check all source and header C++ files; ignore files in hidden directories - name: Execute clang-format diff --git a/.github/workflows/cpp-lint.yml b/.github/workflows/cpp-lint.yml index 65a0aaf..b0f2164 100644 --- a/.github/workflows/cpp-lint.yml +++ b/.github/workflows/cpp-lint.yml @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v3 - uses: jirutka/setup-alpine@v1 with: - branch: v3.19 + branch: v3.21 packages: > cmake ninja-build @@ -44,7 +44,7 @@ jobs: python3-dev fmt-dev gtest-dev - clang-extra-tools + clang19-extra-tools - name: Fixup environment run: | ln -s liblua-5.2.so.0 /usr/lib/liblua-5.2.so diff --git a/examples/multi_language_plugin/multi_language_plugin_manager.cpp b/examples/multi_language_plugin/multi_language_plugin_manager.cpp index dd98d88..44cff2c 100644 --- a/examples/multi_language_plugin/multi_language_plugin_manager.cpp +++ b/examples/multi_language_plugin/multi_language_plugin_manager.cpp @@ -40,12 +40,12 @@ int main(int argc, char* argv[]) plugins.push_back(std::move(*plugin)); } plugins.push_back(manager.loadCPlugin(non_existant_lib_path) - .andThen([](const auto& plugin) { - // convert to generic plugin - return ppplugin::Plugin { plugin }; - }) - // silently fail and use no-op plugin instead - .valueOr(ppplugin::NoopPlugin {})); + .andThen([](const auto& plugin) { + // convert to generic plugin + return ppplugin::Plugin { plugin }; + }) + // silently fail and use no-op plugin instead + .valueOr(ppplugin::NoopPlugin {})); for (auto& plugin : plugins) { plugin.call("initialize").valueOrElse([](const ppplugin::CallError& error) { std::cerr << "Initialize Error: " << error.what() << '\n'; diff --git a/src/boost_dll_loader.cpp b/src/boost_dll_loader.cpp index b89fe0e..8fe878f 100644 --- a/src/boost_dll_loader.cpp +++ b/src/boost_dll_loader.cpp @@ -47,8 +47,7 @@ CallResult getFunctionPointerSymbol(const boost::dll::shared_library& lib return { *error }; } try { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - if (auto* symbol = reinterpret_cast(library.get(function_name))) { + if (auto* symbol = library.get(function_name)) { return symbol; } } catch (const std::exception& exception) { diff --git a/src/python_interpreter.cpp b/src/python_interpreter.cpp index 3ceebe1..05ba348 100644 --- a/src/python_interpreter.cpp +++ b/src/python_interpreter.cpp @@ -95,7 +95,7 @@ PythonInterpreter::PythonInterpreter() PyGILState_Ensure(); #if PY_VERSION_HEX >= 0x030c0000 // Python 3.12 or newer PyThreadState* new_state = nullptr; - PyInterpreterConfig config = _PyInterpreterConfig_INIT; + const PyInterpreterConfig config = _PyInterpreterConfig_INIT; auto status = Py_NewInterpreterFromConfig(&new_state, &config); if (PyStatus_IsError(status) == 0) { state_.reset(new_state); diff --git a/src/python_object.cpp b/src/python_object.cpp index 91b7d77..f2988b5 100644 --- a/src/python_object.cpp +++ b/src/python_object.cpp @@ -2,16 +2,10 @@ #include #include +#include #define PY_SSIZE_T_CLEAN #include // NOLINT(misc-include-cleaner) -#include -#include -#include -#include -#include -#include -#include namespace ppplugin { PythonObject::PythonObject() @@ -152,7 +146,7 @@ std::optional PythonObject::asString() return std::nullopt; } auto length = PyBytes_Size(object()); - return std::string { result, 0, static_cast(length) }; + return std::string { result, static_cast(length) }; } return std::nullopt; } diff --git a/src/python_tuple.cpp b/src/python_tuple.cpp index df9b48a..49afb7a 100644 --- a/src/python_tuple.cpp +++ b/src/python_tuple.cpp @@ -29,8 +29,6 @@ PyObject* PythonTuple::initTuple(int size) return new_tuple; } -// NOLINTBEGIN(bugprone-easily-swappable-parameters) -// NOLINTBEGIN(google-runtime-int) void PythonTuple::setTupleItem(int index, PythonObject value) { auto* py_value = value.pyObject(); diff --git a/test/scope_guard_tests.cpp b/test/scope_guard_tests.cpp index 7a0b1cc..9dc600c 100644 --- a/test/scope_guard_tests.cpp +++ b/test/scope_guard_tests.cpp @@ -29,7 +29,7 @@ TEST(ScopeGuard, moveSourceCalled) { bool guard_called { false }; { - ScopeGuard first_guard { []() {} }; + ScopeGuard first_guard { []() { } }; { ScopeGuard second_guard { [&guard_called]() { @@ -51,7 +51,7 @@ TEST(ScopeGuard, moveTargetCalled) guard_called = true; } }; { - ScopeGuard second_guard { []() {} }; + ScopeGuard second_guard { []() { } }; first_guard = std::move(second_guard); EXPECT_TRUE(guard_called); } From a78ae90de2db6c449d3a2a8677fe29058d77d27b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 27 Apr 2025 00:10:03 +0200 Subject: [PATCH 21/42] python: Remove unofficial CPython includes These includes are not part of the official API and thus can change between different Python versions. This makes them unsuitable to be used by external libraries. Thus, this project will rely on the official Python.h header which includes all the necessary headers. They were originally included to satisfy the include cleaner of clang-tidy. --- src/python_exception.cpp | 2 -- src/python_interpreter.cpp | 14 -------------- src/python_tuple.cpp | 10 ---------- 3 files changed, 26 deletions(-) diff --git a/src/python_exception.cpp b/src/python_exception.cpp index aa9f02c..2a80bd5 100644 --- a/src/python_exception.cpp +++ b/src/python_exception.cpp @@ -12,8 +12,6 @@ #include #include #include -#include -#include namespace ppplugin { std::optional PythonException::latest() diff --git a/src/python_interpreter.cpp b/src/python_interpreter.cpp index 05ba348..62c25df 100644 --- a/src/python_interpreter.cpp +++ b/src/python_interpreter.cpp @@ -16,20 +16,6 @@ #define PY_SSIZE_T_CLEAN #include // NOLINT(misc-include-cleaner) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/src/python_tuple.cpp b/src/python_tuple.cpp index 49afb7a..03c4a26 100644 --- a/src/python_tuple.cpp +++ b/src/python_tuple.cpp @@ -2,19 +2,9 @@ #include "ppplugin/python/python_forward_defs.h" #include -#include -#include -#include #define PY_SSIZE_T_CLEAN #include // NOLINT(misc-include-cleaner) -#include -#include -#include -#include -#include -#include -#include namespace ppplugin { From 36ca8b88efe97b1d28b4cf1760bf387a88ef105c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 27 Apr 2025 00:12:34 +0200 Subject: [PATCH 22/42] errors: Fix and rename CallError::error() to code() The implicit conversion to CallError made it impossible to get the CallError::Code inside the object. Additionally, the name was confusing since the CallError is usually wrapped inside an Expected which also has a method called error(). --- include/ppplugin/errors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ppplugin/errors.h b/include/ppplugin/errors.h index c1d87cb..7551829 100644 --- a/include/ppplugin/errors.h +++ b/include/ppplugin/errors.h @@ -47,7 +47,7 @@ class CallError { { } - [[nodiscard]] CallError error() const { return error_; } + [[nodiscard]] Code code() const { return error_; } [[nodiscard]] const std::string& what() const { return what_; } #ifndef PPPLUGIN_CPP17_COMPATIBILITY [[nodiscard]] const std::source_location& location() const From 1e1753b0a1df8d82fa046e8f98903365de81c749 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 27 Apr 2025 00:15:46 +0200 Subject: [PATCH 23/42] lua: Remove unused LuaType enum --- include/ppplugin/lua/lua_state.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/ppplugin/lua/lua_state.h b/include/ppplugin/lua/lua_state.h index 2480d45..86a3063 100644 --- a/include/ppplugin/lua/lua_state.h +++ b/include/ppplugin/lua/lua_state.h @@ -14,15 +14,6 @@ struct lua_State; namespace ppplugin { -enum class LuaType { - none = -1, - nil = 0, - boolean = 1, - number = 3, - string = 4, - function = 6, -}; - class LuaState { public: LuaState(); From a20b7d91cc49a40bb4274ba29afe73cafd70a5fb Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 27 Apr 2025 17:23:41 +0200 Subject: [PATCH 24/42] templates: Add IsSpecializationV as abbreviation of IsSpecialization<>::value --- include/ppplugin/detail/template_helpers.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/ppplugin/detail/template_helpers.h b/include/ppplugin/detail/template_helpers.h index d8efbf3..b09f224 100644 --- a/include/ppplugin/detail/template_helpers.h +++ b/include/ppplugin/detail/template_helpers.h @@ -185,6 +185,10 @@ struct IsSpecialization : std::false_type { }; template