Implement a basic plugin system using dynamic libraries
This commit is contained in:
parent
921f12a5f8
commit
5f64836b0c
61
Makefile
Normal file
61
Makefile
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# ---------- Variables listed below --------- #
|
||||||
|
|
||||||
|
# Target directory
|
||||||
|
BUILD = build
|
||||||
|
# Paths to source, include, dependency, and object files
|
||||||
|
SRC = src
|
||||||
|
INC = inc
|
||||||
|
DEP = $(BUILD)/dep
|
||||||
|
OBJ = $(BUILD)/obj
|
||||||
|
ALL = $(SRC) $(INC) $(BUILD) $(DEP) $(OBJ)
|
||||||
|
|
||||||
|
# Executable
|
||||||
|
TARGET := $(BUILD)/main.out $(BUILD)/demo.so
|
||||||
|
|
||||||
|
# compiler and compiler flags
|
||||||
|
CC = g++
|
||||||
|
CFLAGS = -I$(INC)
|
||||||
|
SOFLAG = -fpic -shared -dynamic -ldl
|
||||||
|
|
||||||
|
# list of static object files
|
||||||
|
SOURCES = $(wildcard $(SRC)/*.cpp)
|
||||||
|
OBJECTS = $(addprefix $(OBJ)/,$(notdir $(SOURCES:.cpp=.o)))
|
||||||
|
|
||||||
|
# ----------- Targets listed below ---------- #
|
||||||
|
# Some targets aren't real
|
||||||
|
.PHONY: all clean run dump
|
||||||
|
# Don't autodelete object files:
|
||||||
|
.PRECIOUS: $(OBJ)/%.o
|
||||||
|
|
||||||
|
all: $(DEP) $(OBJ) $(TARGET)
|
||||||
|
|
||||||
|
dump:
|
||||||
|
@echo "SOURCES: $(SOURCES)"
|
||||||
|
@echo "OBJECTS: $(OBJECTS)"
|
||||||
|
@echo " TARGET: $(TARGET)"
|
||||||
|
@echo " PATHS: $(ALL)"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -r $(BUILD)
|
||||||
|
|
||||||
|
run: $(TARGET)
|
||||||
|
-$(addprefix ./,$(addsuffix ;,$(TARGET)))
|
||||||
|
|
||||||
|
# Create directories for output
|
||||||
|
$(BUILD) $(DEP) $(OBJ):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
# Make the executable
|
||||||
|
$(BUILD)/%.out: $(OBJECTS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
# Make the plugins
|
||||||
|
$(BUILD)/%.so: $(OBJ)/%.o
|
||||||
|
$(CC) $(CFLAGS) $(SOFLAG) -o $@ $^
|
||||||
|
# Make the object and dependency files
|
||||||
|
$(OBJ)/%.o: $(SRC)/%.cpp
|
||||||
|
$(CC) $(CFLAGS) -MMD -MF $(DEP)/$(@F:.o=.d) -o $@ -c $<
|
||||||
|
|
||||||
|
|
||||||
|
# --------- Inclusions listed below --------- #
|
||||||
|
# use dependencies when rebuilding
|
||||||
|
-include $(wildcard $(DEP)/*.d)
|
4
inc/lmao_syntax.hpp
Normal file
4
inc/lmao_syntax.hpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#define let auto
|
||||||
|
#define is ==
|
||||||
|
#define None nullptr
|
25
inc/plugin.hpp
Normal file
25
inc/plugin.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static char* plugin_error;
|
||||||
|
|
||||||
|
// define a schema for plugins
|
||||||
|
class Plugin {
|
||||||
|
private:
|
||||||
|
const char* name;
|
||||||
|
void* handle;
|
||||||
|
void* (*fn_init)();
|
||||||
|
int (*fn_call)(void*, int argc, char* argv[]);
|
||||||
|
int (*fn_exit)(void*);
|
||||||
|
void* data;
|
||||||
|
// Constructor
|
||||||
|
void init (char* so_path);
|
||||||
|
public:
|
||||||
|
Plugin (char* path);
|
||||||
|
Plugin (std::string path);
|
||||||
|
Plugin (Plugin&&) = default;
|
||||||
|
~Plugin ();
|
||||||
|
int call (int argc, char* argv[]);
|
||||||
|
int operator()(int argc, char* argv[]);
|
||||||
|
};
|
24
src/demo.cpp
Normal file
24
src/demo.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// demo shared object
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
static int g = 0;
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
int number;
|
||||||
|
};
|
||||||
|
extern "C" {
|
||||||
|
void* plugin_init () {
|
||||||
|
return new Data{ g++ };
|
||||||
|
}
|
||||||
|
|
||||||
|
int plugin_main (void* data, int argc, char** argv) {
|
||||||
|
printf ("Hello from %s!\n", __FILE__);
|
||||||
|
printf ("The data is %d!\n", ((Data*)data)->number);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int plugin_exit (void* data) {
|
||||||
|
delete (Data*)data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
30
src/main.cpp
Normal file
30
src/main.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "lmao_syntax.hpp"
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
int main (int argc, char* argv[]) {
|
||||||
|
using namespace std;
|
||||||
|
string plugin_file;
|
||||||
|
vector<Plugin> plugins;
|
||||||
|
while (true) {
|
||||||
|
printf ("> ");
|
||||||
|
getline (cin, plugin_file);
|
||||||
|
if (plugin_file.empty ()) continue;
|
||||||
|
if (plugin_file == "clear") {
|
||||||
|
plugins.clear ();
|
||||||
|
printf ("Cleared.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
plugins.emplace_back (plugin_file);
|
||||||
|
plugins.back ()(0, {});
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what () << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/plugin.cpp
Normal file
59
src/plugin.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include <stdexcept>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "lmao_syntax.hpp"
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
|
// Throws domain error
|
||||||
|
void* resolve (void* handle, const char* name) {
|
||||||
|
void* symbol = dlsym (handle, (char*)name);
|
||||||
|
if (symbol is None) {
|
||||||
|
throw std::runtime_error (dlerror ());
|
||||||
|
}
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plugin::init (char* so_path) {
|
||||||
|
//dlopen the plugin
|
||||||
|
handle = dlopen (so_path, RTLD_NOW);
|
||||||
|
if (handle is None) {
|
||||||
|
throw std::invalid_argument (dlerror ());
|
||||||
|
}
|
||||||
|
printf ("handle: %p\n", handle);
|
||||||
|
//find its plugin struct
|
||||||
|
fn_init = (void* (*)()) resolve (handle, "plugin_init");
|
||||||
|
fn_exit = (int (*)(void*)) resolve (handle, "plugin_exit");
|
||||||
|
fn_call = (int (*)(void*, int, char* [])) resolve (handle, "plugin_main");
|
||||||
|
|
||||||
|
data = fn_init ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::Plugin (char* so_path) {
|
||||||
|
init (so_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::Plugin (std::string so_path) {
|
||||||
|
init ((char*)so_path.c_str ());
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::~Plugin () {
|
||||||
|
printf ("Destroyed %p\n", handle);
|
||||||
|
if (!handle) {
|
||||||
|
printf ("tried to unload nonexistent plugin\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dlclose (handle)) {
|
||||||
|
printf ("failed to close plugin: %s", dlerror ());
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Plugin::call (int argc, char* argv[]) {
|
||||||
|
return fn_call (data, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Plugin::operator()(int argc, char* argv[]) {
|
||||||
|
return call (argc, argv);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user