Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@

add_subdirectory(cpp_plugin)
10 changes: 10 additions & 0 deletions examples/cpp_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
add_executable(cpp_example_1 example_1.cpp)
target_link_libraries(cpp_example_1 ${LIBRARY_TARGET})

add_executable(cpp_example_2 example_2.cpp)
target_link_libraries(cpp_example_2 ${LIBRARY_TARGET})

add_executable(cpp_example_3 example_3.cpp)
target_link_libraries(cpp_example_3 ${LIBRARY_TARGET})

add_library(cpp_plugin SHARED cpp_plugin.cpp)
50 changes: 50 additions & 0 deletions examples/cpp_plugin/cpp_plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "cpp_plugin.h"

#include <chrono>
#include <iostream>
#include <memory>

#include <boost/dll/alias.hpp>

BOOST_DLL_ALIAS(SimplePluginA::create, create_a);

int global_variable = 0;

void SimplePluginA::loop()
{
while (true) {
doSomething();
}
}

void SimplePluginA::doSomething()
{
std::cout << "A: " << i_ << ", global: " << global_variable << '\n';
sleep(std::chrono::seconds { 1 });
}

class SimplePluginB : public SimplePluginInterface {
public:
SimplePluginB() = default;

static std::shared_ptr<SimplePluginB> create()
{
return std::make_shared<SimplePluginB>();
}

void initialize(int value) override { i_ = std::make_unique<int>(value); }
void doSomething() override
{
std::cout << "B: " << *i_ << ", global: " << global_variable << '\n';
sleep(std::chrono::seconds { 2 });
}

// cannot be accessed by program that loads this plugin because
// it is hidden in this source file without a virtual function
// in the parent class
void inaccessible() { }

private:
std::unique_ptr<int> i_;
};
BOOST_DLL_ALIAS(SimplePluginB::create, create_b);
49 changes: 49 additions & 0 deletions examples/cpp_plugin/cpp_plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef PPPLUGIN_EXAMPLES_CPP_PLUGIN_H
#define PPPLUGIN_EXAMPLES_CPP_PLUGIN_H

#include <chrono>
#include <memory>
#include <thread>

class SimplePluginInterface {
public:
SimplePluginInterface() = default;
virtual ~SimplePluginInterface() = default;
SimplePluginInterface(const SimplePluginInterface&) = default;
SimplePluginInterface(SimplePluginInterface&&) noexcept = default;
SimplePluginInterface& operator=(const SimplePluginInterface&) = default;
SimplePluginInterface& operator=(SimplePluginInterface&&) noexcept = default;

virtual void initialize(int /*value*/) { }

virtual void doSomething() = 0;

protected:
static void sleep(std::chrono::milliseconds duration)
{
std::this_thread::sleep_for(duration);
}
};

class SimplePluginA : public SimplePluginInterface {
public:
SimplePluginA() = default;

static std::shared_ptr<SimplePluginA> create()
{
return std::make_shared<SimplePluginA>();
}

// must be virtual to be accessible for the program that loads this plugin;
// otherwise, trying to call the function will cause a linker error
virtual void loop();

void doSomething() override;

private:
int i_ { 0 };
};

extern int global_variable;

#endif // PPPLUGIN_EXAMPLES_CPP_PLUGIN_H
33 changes: 33 additions & 0 deletions examples/cpp_plugin/example_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>

// library headers
#include "ppplugin/cpp/plugin.h"
#include "ppplugin/errors.h"

// header of example plugin
#include "cpp_plugin.h"

int main()
{
auto&& load_result = ppplugin::CppPlugin::load("./libcpp_plugin.so");
if (!load_result) {
std::cerr << load_result.error() << '\n';
return 1;
}

// loading plugin was successful, call a function that returns "std::shared_ptr<SimplePluginA>"
auto&& plugin = load_result.value();
auto&& call_result = plugin.call<std::shared_ptr<SimplePluginA>>("create_a");
if (!call_result) {
std::cerr << call_result.error() << '\n';
return 2;
}

// unwrap instance of newly created SimplePluginA and call function that was
// unknown to this program until the plugin was loaded (defined in cpp_plugin.cpp)
auto&& simple_plugin_a = call_result.value();
simple_plugin_a->loop();
}
41 changes: 41 additions & 0 deletions examples/cpp_plugin/example_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <cstdlib>
#include <exception>
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>
#include <thread>

#include "ppplugin/cpp/plugin.h"
#include "ppplugin/errors.h"
#include "ppplugin/plugin_manager.h"

#include "cpp_plugin.h"

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

auto simple_plugin_a = plugin.call<std::shared_ptr<SimplePluginA>>("create_a").valueOr(nullptr);
// create instance of type SimplePluginB which is fully unknown to this program;
// the entire class definition is in cpp_plugin.cpp which is not compiled into this binary
auto simple_plugin_b = plugin.call<std::shared_ptr<SimplePluginInterface>>("create_b").valueOr(nullptr);

if (simple_plugin_a) {
simple_plugin_a->initialize(1000);
simple_plugin_a->doSomething();
}
if (simple_plugin_b) {
simple_plugin_a->initialize(2000);
simple_plugin_b->doSomething();
}
}
68 changes: 68 additions & 0 deletions examples/cpp_plugin/example_3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <cstdlib>
#include <exception>
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>
#include <thread>

#include "ppplugin/cpp/plugin.h"
#include "ppplugin/errors.h"
#include "ppplugin/plugin_manager.h"

#include "cpp_plugin.h"

namespace {
std::mutex output_mutex;

template <typename ClassType>
std::thread initializeAndLoop(ppplugin::CppPlugin& plugin, const std::string& function_name)
{
auto result = plugin.call<std::shared_ptr<ClassType>>(function_name);
if (result) {
auto& plugin = *result;
plugin->initialize(100);
return std::thread { [plugin]() {
while (true) {
std::lock_guard lock { output_mutex };
plugin->doSomething();
}
} };
}
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[])
{
try {
if (argc < 1) {
return -1;
}
auto executable_dir = std::filesystem::path { argv[0] }.parent_path();
auto library_path = executable_dir / "libcpp_plugin.so";
// setup manager - has to stay alive for as long as you want to use the plugins
ppplugin::PluginManager manager;
// load plugin library and exit on error
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.code());
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");
thread_a.join();
thread_b.join();
} catch (const std::exception& exception) {
std::cerr << "A fatal error occurred: '" << exception.what() << "'\n";
return 1;
} catch (...) {
std::cerr << "An unknown fatal error occurred!\n";
return 1;
}
return 0;
}
Loading