Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
99f064b
lua: Add global() functions to set/get global variables
taminob Nov 27, 2024
3dcc4a8
cpp: First draft for set/get global variables
taminob Nov 27, 2024
126fee5
c: Add temporary version for global()
taminob Nov 27, 2024
8a537cb
python: Add dummy global() functions
taminob Nov 27, 2024
bd7be12
GenericPlugin: Add global() function
taminob Nov 27, 2024
b63a916
NoopPlugin: Add global() implementation
taminob Feb 18, 2025
4154a2c
templates: Add SecondTemplateParameterOr helper
taminob Feb 18, 2025
7541a19
CallError|LuaState: Add incorrectType error code
taminob Feb 18, 2025
24a88ea
templates: Add C++17 compatible helper for std::remove_cvref_t
taminob Feb 19, 2025
a3c64ec
python: Move C++ type to PyObject conversion to PythonObject
taminob Feb 19, 2025
6146756
python: Implement global() setter and getter
taminob Feb 19, 2025
1f62971
python: Fix conversion with null bytes in bytes object
taminob Feb 19, 2025
3af5a12
cpp: Remove empty CppObjectPlugin
taminob Feb 19, 2025
703f04b
test: Add some Expected::andThen() tests
taminob Feb 19, 2025
11d054c
src: Remove empty and unused ppplugin.cpp file
taminob Mar 30, 2025
dcadabd
Plugin: Return error for setter of global variables
taminob Mar 30, 2025
fe94da9
everything: Move remaining member function definitions out of class d…
taminob Mar 30, 2025
0a14b9e
python|lua|plugin: Add nodiscard modifier to member functions
taminob Mar 30, 2025
236842a
clang-tidy: Remove include-cleaner because of Python
taminob Mar 30, 2025
fb54977
ci: Update clang-tidy/clang-format and fix affected files
taminob Mar 30, 2025
a78ae90
python: Remove unofficial CPython includes
taminob Apr 26, 2025
36ca8b8
errors: Fix and rename CallError::error() to code()
taminob Apr 26, 2025
1e1753b
lua: Remove unused LuaType enum
taminob Apr 26, 2025
a20b7d9
templates: Add IsSpecializationV as abbreviation of IsSpecialization<…
taminob Apr 27, 2025
3a55b10
lua: Add support for std::map/std::vector in push()/top()
taminob Apr 27, 2025
534149d
lua: Add dumpStack() helper function
taminob Apr 27, 2025
9a02eb8
test: Add lua_tests
taminob Apr 27, 2025
2294b73
python: Allow lock/unlock only in PythonGuard
taminob Apr 27, 2025
d58f680
python: Fix function call return value conversion
taminob Apr 27, 2025
9028ac8
test: Add python_tests
taminob Apr 27, 2025
119e4b4
test: Move errorOutput from lua_tests to test_helper and fix cmake cu…
taminob Apr 27, 2025
431a984
python: Fix ownership issue in PythonTuple::setTupleItem()
taminob Apr 27, 2025
e12e41e
python: Fix this capture in moved instance bug
taminob Apr 27, 2025
39e04cf
python: Do not use unofficial _PyInterpreterConfig_INIT
taminob Apr 27, 2025
717b23f
python: Add list/dict support to PythonObject
taminob Apr 29, 2025
3c20457
test: Add basic python tests for dicts/lists
taminob Apr 29, 2025
dee2949
python: Add char conversion
taminob Apr 29, 2025
b0a20f4
lua|cmake: Minor lint/compilation fixes
taminob Apr 29, 2025
b78d1f4
test: Add python global() tests
taminob Apr 29, 2025
c4aae6e
errors|examples: Add codeToString()
taminob May 1, 2025
197706c
everything: Minor linting/formatting fixes
taminob May 1, 2025
7f37b0b
test: Make errorOutput() C++17 compatible
taminob May 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ 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,
concurrency-*,
cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,
modernize-*,-modernize-use-trailing-return-type,-modernize-use-emplace,
modernize-*,-modernize-use-trailing-return-type,-modernize-use-emplace,-modernize-use-designated-initializers,
performance-*,-performance-noexcept-move-constructor,
portability-*,
readability-*,-readability-redundant-access-specifiers
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cpp-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cpp-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 2 additions & 4 deletions examples/configurable_plugin/configurable_plugin_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ int main(int argc, char* argv[])
if (!a_1 || !a_2) {
return 2;
}
auto unwrapped_a_1 = *a_1;
auto unwrapped_a_2 = *a_2;

std::atomic<bool> stop_signal { false };
std::thread thread_1([plugin = unwrapped_a_1, &stop_signal]() { plugin->loop(stop_signal); });
std::thread thread_2([plugin = unwrapped_a_2, &stop_signal]() { plugin->loop(stop_signal); });
std::thread thread_1([plugin = *a_1, &stop_signal]() { plugin->loop(stop_signal); });
std::thread thread_2([plugin = *a_2, &stop_signal]() { plugin->loop(stop_signal); });
std::this_thread::sleep_for(std::chrono::seconds { 5 });
stop_signal.store(true);
thread_1.join();
Expand Down
14 changes: 7 additions & 7 deletions examples/multi_language_plugin/multi_language_plugin_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>("initialize").valueOrElse([](const ppplugin::CallError& error) {
std::cerr << "Initialize Error: " << error.what() << '\n';
Expand All @@ -54,7 +54,7 @@ int main(int argc, char* argv[])
for (int counter {}; counter < std::numeric_limits<int>::max(); ++counter) {
for (auto& plugin : plugins) {
// explicit cast to int to avoid passing by reference
plugin.call<void>("loop", static_cast<int>(counter))
plugin.call<void>("loop", static_cast<int>(counter)) // NOLINT(readability-redundant-casting)
.valueOrElse([](const ppplugin::CallError& error) {
std::cerr << "Loop Error: " << error.what() << '\n';
});
Expand Down
2 changes: 1 addition & 1 deletion examples/multi_language_plugin/plugins/cpp_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include <boost/dll/alias.hpp>

void initialize()
extern void initialize()
{
std::cout << "C++ initialize" << '\n';
}
Expand Down
47 changes: 14 additions & 33 deletions examples/simple_plugin/simple_plugin_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <thread>

#include "ppplugin/cpp/plugin.h"
Expand All @@ -13,43 +12,25 @@

#include "simple_plugin.h"

void printError(std::string_view function_name, const ppplugin::CallError& error)
{
const std::string_view error_string = error == ppplugin::CallError::Code::symbolNotFound ? "not found"
: error == ppplugin::CallError::Code::notLoaded ? "not loaded"
: error == ppplugin::CallError::Code::unknown ? "unknown error"
: "other error";
std::cerr << "Unable to call '" << function_name << "' ('" << error_string
<< "')!\n";
}

void printError(std::string_view plugin_name, ppplugin::LoadError error)
{
using ppplugin::LoadError;
const std::string_view error_string = error == LoadError::fileNotFound ? "not found"
: error == LoadError::fileInvalid ? "invalid"
: error == LoadError::unknown ? "unknown error"
: "other error";
std::cerr << "Unable to load '" << plugin_name << "' ('" << error_string
<< "')!\n";
}

namespace {
template <typename ClassType>
std::thread initializeAndLoop(ppplugin::CppPlugin& plugin, const std::string& function_name)
{
auto plugin_a = plugin.call<std::shared_ptr<ClassType>>(function_name);
if (plugin_a) {
auto& plugin = *plugin_a;
auto result = plugin.call<std::shared_ptr<ClassType>>(function_name);
if (result) {
auto& plugin = *result;
plugin->initialize();
return std::thread { [plugin]() {
plugin->loop();
} };
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access); checked in previous if
return std::thread { [function_name, error = *plugin_a.error()]() {
printError(function_name, error);
return std::thread { [function_name, error = *result.error()]() {
std::cerr << "Unable to call '" << function_name
<< "' ('" << ppplugin::codeToString(error.code()) << "')!\n";
} };
}
} // namespace

int main(int argc, char* argv[])
{
Expand All @@ -59,14 +40,14 @@ int main(int argc, char* argv[])
}
auto executable_dir = std::filesystem::path { argv[0] }.parent_path();
auto library_path = executable_dir / "libsimple_plugin.so";
// setup manager - has to stay alive for as long as you want to use the
// plugins
// setup manager - has to stay alive for as long as you want to use the plugins
ppplugin::GenericPluginManager<SimplePluginInterface> manager;
// load plugin library and exit on error
auto plugin_library = manager.loadCppPlugin(std::filesystem::path { library_path }).valueOrElse([](const auto& error) -> ppplugin::CppPlugin {
printError("simple_plugin_a", error);
std::exit(1); // NOLINT(concurrency-mt-unsafe)
});
auto plugin_library = manager.loadCppPlugin(std::filesystem::path { library_path })
.valueOrElse([](const auto& error) -> ppplugin::CppPlugin {
std::cerr << "Unable to load plugin: " << ppplugin::codeToString(error);
std::exit(1); // NOLINT(concurrency-mt-unsafe)
});

auto thread_a = initializeAndLoop<SimplePluginA>(plugin_library, "create_a");
auto thread_b = initializeAndLoop<SimplePluginInterface>(plugin_library, "create_b");
Expand Down
66 changes: 45 additions & 21 deletions include/ppplugin/c/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,60 @@
namespace ppplugin {
class CPlugin {
public:
static Expected<CPlugin, LoadError> 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<CPlugin, LoadError> load(const std::filesystem::path& plugin_library_path);

// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
operator bool() const;

template <typename ReturnValue, typename... Args>
[[nodiscard]] CallResult<ReturnValue> call(const std::string& function_name, Args&&... args)
{
static_assert(!std::is_reference_v<ReturnValue>,
"C does not support references for its return value!");
static_assert(!(std::is_reference_v<Args> || ...),
"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<false, ReturnValue>(
plugin_, function_name, std::forward<Args>(args)...);
}
[[nodiscard]] CallResult<ReturnValue> call(const std::string& function_name, Args&&... args);

template <typename VariableType>
[[nodiscard]] CallResult<VariableType> global(const std::string& variable_name);
template <typename VariableType>
[[nodiscard]] CallResult<void> global(const std::string& variable_name, VariableType&& new_value);

private:
CPlugin() = default;

private:
boost::dll::shared_library plugin_;
};

template <typename ReturnValue, typename... Args>
CallResult<ReturnValue> CPlugin::call(const std::string& function_name, Args&&... args)
{
static_assert(!std::is_reference_v<ReturnValue>,
"C does not support references for its return value!");
static_assert(!(std::is_reference_v<Args> || ...),
"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<false, ReturnValue>(
plugin_, function_name, std::forward<Args>(args)...);
}

// TODO: remove code duplication here and in C++ plugin
template <typename VariableType>
CallResult<VariableType> CPlugin::global(const std::string& variable_name)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
return *reinterpret_cast<VariableType*>(p.value().value());
}
return { p.error().value() };
}

template <typename VariableType>
CallResult<void> CPlugin::global(const std::string& variable_name, VariableType&& new_value)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
*reinterpret_cast<VariableType*>(p.value().value()) = std::forward<VariableType>(new_value);
return {};
}
return { p.error().value() };
}
} // namespace ppplugin

#endif // PPPLUGIN_C_PLUGIN_H
16 changes: 0 additions & 16 deletions include/ppplugin/cpp/object_plugin.h

This file was deleted.

55 changes: 36 additions & 19 deletions include/ppplugin/cpp/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,7 @@
namespace ppplugin {
class CppPlugin {
public:
static Expected<CppPlugin, LoadError> 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<CppPlugin, LoadError> load(const std::filesystem::path& plugin_library_path);

~CppPlugin() = default;
CppPlugin(const CppPlugin&) = default;
Expand All @@ -30,24 +21,50 @@ class CppPlugin {
CppPlugin& operator=(CppPlugin&&) = default;

// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
operator bool() const
{
return plugin_.is_loaded();
}
operator bool() const;

template <typename ReturnValue, typename... Args>
[[nodiscard]] CallResult<ReturnValue> call(const std::string& function_name, Args&&... args)
{
return detail::boost_dll::call<true, ReturnValue>(
plugin_, function_name, std::forward<Args>(args)...);
}
[[nodiscard]] CallResult<ReturnValue> call(const std::string& function_name, Args&&... args);

template <typename VariableType>
[[nodiscard]] CallResult<VariableType> global(const std::string& variable_name);
template <typename VariableType>
[[nodiscard]] CallResult<void> global(const std::string& variable_name, VariableType&& new_value);

private:
CppPlugin() = default;

private:
boost::dll::shared_library plugin_;
};

template <typename ReturnValue, typename... Args>
CallResult<ReturnValue> CppPlugin::call(const std::string& function_name, Args&&... args)
{
return detail::boost_dll::call<true, ReturnValue>(
plugin_, function_name, std::forward<Args>(args)...);
}

template <typename VariableType>
CallResult<VariableType> CppPlugin::global(const std::string& variable_name)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
return *reinterpret_cast<VariableType*>(p.value().value());
}
return { p.error().value() };
}

template <typename VariableType>
CallResult<void> CppPlugin::global(const std::string& variable_name, VariableType&& new_value)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
*reinterpret_cast<VariableType*>(p.value().value()) = std::forward<VariableType>(new_value);
return {};
}
return { p.error().value() };
}
} // namespace ppplugin

#endif // PPPLUGIN_CPP_PLUGIN_H
1 change: 1 addition & 0 deletions include/ppplugin/detail/boost_dll_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace ppplugin::detail::boost_dll {

[[nodiscard]] CallResult<void*> getFunctionSymbol(const boost::dll::shared_library& library, const std::string& function_name);
[[nodiscard]] CallResult<void*> getFunctionPointerSymbol(const boost::dll::shared_library& library, const std::string& function_name);
[[nodiscard]] CallResult<void**> getSymbol(const boost::dll::shared_library& library, const std::string& function_name);

template <bool isPointer, typename ReturnValue, typename... Args>
[[nodiscard]] CallResult<ReturnValue> call(const boost::dll::shared_library& library, const std::string& function_name, Args&&... args)
Expand Down
Loading