С++ плагины

В этой заметке я опишу пример добавления функционала в C++ приложение с помощью плагинов. Описана практическая часть реализации для Linux, с теорией можно будет ознакомиться по ссылкам в конце статьи.

Composition over inheritance!

Для начала напишем плагин – функцию которую будем вызывать:

#include "iostream"

using namespace std;

extern "C" void extensionEntryPoint() {
	cout << "Extension entry point called" << endl;
};

Далее соберем плагин как динамическую библиотеку “extension.so”, которую и будем подключать в дальнейшем:
clang++ -shared -fPIC extension.cpp -o extension.so

Напишем основое приложение, которое будет загружать файл “extension.so”, искать там указатель на функцию “extensionEntryPoint”, и вызывать его, печатая ошибки при необходимости:

#include "iostream"
#include "dlfcn.h"

using namespace std;

typedef void (*VoidFunctionPointer)();	

int main (int argc, char *argv[]) {

	cout << "C++ Plugins Example" << endl;

	auto extensionHandle = dlopen("./extension.so", RTLD_LAZY);
	if (!extensionHandle) {
		string errorString = dlerror();
		throw runtime_error(errorString);
	}

	auto functionPointer = VoidFunctionPointer();
	functionPointer = (VoidFunctionPointer) dlsym(extensionHandle, "extensionEntryPoint");
	auto dlsymError = dlerror();
 	if (dlsymError) {
		string errorString = dlerror();
		throw runtime_error(errorString);
 	}

	functionPointer();

	exit(0);
} 

Функция dlopen возвращает хэндлер для работы с динамической библиотекой; функция dlsym возвращает указатель на необходимую функцию по строке; dlerror содержит указатель на строку с текстом ошибки, если таковая имеется.

Далее собираем основное приложение, копируем файл динамической библиотеки в папку с ним и запускаем. На выходе должен быть вывод “Extension entry point called”

К сложным моментам можно отнести отсутствие единого стандарта работы с динамическими библиотеками, из-за этого есть необходимость экспорта функции в относительно глобальную область видимости с extern C; разница в работе с разными операционными системами, связанные с этим тонкости работы; отсутствие C++ интерфейса для реализации ООП подхода к работе с динамическими библиотеками, однако существуют open-source врапперы, например m-renaud/libdlibxx

Исходный код примера

https://gitlab.com/demensdeum/cpppluginsexample

Источники

http://man7.org/linux/man-pages/man3/dlopen.3.htm
https://gist.github.com/tailriver/30bf0c943325330b7b6a
https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work