feat: 用Boost.DLL从指定目录用加载动态链接库!

This commit is contained in:
Creeperxie 2025-02-28 21:14:32 +08:00
commit c5204ba3a0
Signed by: creeperxie
GPG Key ID: ACB4CA11358CC794
10 changed files with 150 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# clangd的缓存目录
.cache
# 为clangd创建的compile_commands.json符号链接
compile_commands.json

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# loader
`/usr/lib/loader``/usr/local/lib/loader`里的动态链接库作为插件加载!
可以通过`LOADER_PLUGIN_DIRS`覆盖默认插件目录!
Linux下用`:`分割路径Windows下用`;`分割路径。
为了方便测试,用
[just](https://github.com/casey/just)
封装了meson
```console
$ meson setup builddir
$ just install
$ /usr/local/bin/loader
```

11
include/interface.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include <boost/config.hpp>
class BOOST_SYMBOL_VISIBLE Interface {
public:
virtual const std::string name() const = 0;
virtual ~Interface() {}
};

5
justfile Normal file
View File

@ -0,0 +1,5 @@
build:
meson compile -C builddir
install: build
meson install -C builddir

61
main.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/dll/import.hpp>
#include "interface.hpp"
using std::string;
using std::vector;
namespace fs = boost::filesystem;
namespace dll = boost::dll;
const vector<string> &get_plugin_dirs() {
static vector<string> result;
if (!result.empty()) {
return result;
}
#ifdef _WIN32
const string delimiter = ";";
#else
const string delimiter = ":";
#endif
const char *plugin_dirs = std::getenv("LOADER_PLUGIN_DIRS");
if (plugin_dirs != nullptr && *plugin_dirs != '\0') {
boost::split(result, plugin_dirs, boost::is_any_of(delimiter));
}
if (result.empty()) {
result.push_back("/usr/lib/loader");
result.push_back("/usr/local/lib/loader");
}
return result;
}
int main() {
const vector<string> &plugin_dirs = get_plugin_dirs();
for (const auto &plugin_dir : plugin_dirs) {
fs::path plugin_path(plugin_dir);
if (!fs::is_directory(plugin_path)) {
continue;
}
for (fs::recursive_directory_iterator dir(plugin_path), end; dir != end; ++dir) {
boost::shared_ptr<Interface> plugin;
plugin = dll::import_symbol<Interface>(
dir->path(),
"plugin",
dll::load_mode::rtld_lazy
);
std::cout << "loading plugin: " << plugin->name() << std::endl;
}
}
}

13
meson.build Normal file
View File

@ -0,0 +1,13 @@
project('loader', 'cpp',
default_options: ['cpp_std=c++20'])
inc = include_directories('include')
plugin_dep = declare_dependency(include_directories : inc)
boost_dep = dependency('boost', modules : ['filesystem'])
executable('loader', 'main.cpp',
include_directories : inc,
dependencies : boost_dep,
install : true)
subdir('plugins')

View File

@ -0,0 +1,4 @@
command = shared_library('command', 'plugin.cpp',
dependencies : plugin_dep,
install : true,
install_dir : 'lib/loader')

View File

@ -0,0 +1,17 @@
#include "plugin.hpp"
#include <iostream>
#include <string>
using std::string;
namespace loader {
const string Command::name() const {
return "command";
}
Command::~Command() {
std::cout << "destructing command" << std::endl;
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <string>
#include "interface.hpp"
namespace loader {
class Command : public Interface {
public:
const std::string name() const;
~Command();
};
extern "C" BOOST_SYMBOL_EXPORT Command plugin;
Command plugin;
}

1
plugins/meson.build Normal file
View File

@ -0,0 +1 @@
subdir('command')