Coding — Создание плагинов на C
Недавно заинтересовался тем, как создаются плагины для программ. Мне нравится язык C, поэтому я в первую очередь решил найти решение для него. В посте расскажу о принципах реализации и о такой вещи как универсализация.
Листинг main.c, в котором содержится основная программа:
Теперь разберем, что здесь понаписано.
Для использования библиотеки, с помощью которой мы будем подключать динамические библиотеки (читать плагины) во время выполнения программы.
А сейчас пару слов про универсализацию. Каждый человек который работает в консоли знает что означают * и ? при написании имени файла. Так вот, универсализация это процесс поиска файлов попадающих под заданный шаблон и заголовочный файл
позволяет нам это делать в нашей программе.
Создаем тип указателя на функцию, которую будем вызывать из плагина.
Функция обрабатывающая ошибки универсализации. В данном случае просто выводит сообщение об ошибке. При возвращении из этой функции нуля ошибка считается обработанной и универсализация продолжается, иначе выполнение прерывается.
Ищем файлы в папке plugins относительно текущей папки программы. Результат занисится в result.
Проверяем хватило ли места в памяти.
Далее, мы проходимся циклом по всем файлам, которые нашли.
Пытаемся открыть библиотеку. Параметр RTLD_LAZY отвечает за процесс поиска всех символов в библиотеке. В данном случае она не будет производиться при открытии библиотеки. Если вы будете открывать большие библиотеки рекомендую использовать именно этот параметр т.к. в другом случае может снизиться скорость работы.
Проверяем на ошибки.
Очищаем прежнее содержание функции dlerro() и пытаемся найти в библиотеке символ print (в данном случае нашу функцию).
Опять проверяем на ошибки.
Если все нормально мы наконец можем вызвать нужную нам функцию и закрыть библиотеку.
Освобождаем память занятую при универсализации.
Листинг hello_world.c, который и станет нашим плагином:
Ну тут думаю все должно быть понятно.
Makefile к main.c:
Makefile к hello_world.c:
Вот и всё =)
Листинг main.c, в котором содержится основная программа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <stdio.h> |
Теперь разберем, что здесь понаписано.
#include <dlfcn.h>
Для использования библиотеки, с помощью которой мы будем подключать динамические библиотеки (читать плагины) во время выполнения программы.
А сейчас пару слов про универсализацию. Каждый человек который работает в консоли знает что означают * и ? при написании имени файла. Так вот, универсализация это процесс поиска файлов попадающих под заданный шаблон и заголовочный файл
#include <glob.h>
позволяет нам это делать в нашей программе.
typedef void (*print_function)(void);
Создаем тип указателя на функцию, которую будем вызывать из плагина.
1 2 3 4 5 |
int errfn(const char * pathname, int theerr) { |
Функция обрабатывающая ошибки универсализации. В данном случае просто выводит сообщение об ошибке. При возвращении из этой функции нуля ошибка считается обработанной и универсализация продолжается, иначе выполнение прерывается.
1 2 3 4 5 6 7 |
glob_t result; // результат универсализации |
rc = glob("plugins/*.so", flags, errfn, &result);
Ищем файлы в папке plugins относительно текущей папки программы. Результат занисится в result.
1 2 3 4 |
if (rc == GLOB_NOSPACE) { |
Проверяем хватило ли места в памяти.
Далее, мы проходимся циклом по всем файлам, которые нашли.
lib = dlopen(result.gl_pathv[i], RTLD_LAZY);
Пытаемся открыть библиотеку. Параметр RTLD_LAZY отвечает за процесс поиска всех символов в библиотеке. В данном случае она не будет производиться при открытии библиотеки. Если вы будете открывать большие библиотеки рекомендую использовать именно этот параметр т.к. в другом случае может снизиться скорость работы.
1 2 3 4 |
if (lib == NULL) { |
Проверяем на ошибки.
1 2 |
dlerror(); |
Очищаем прежнее содержание функции dlerro() и пытаемся найти в библиотеке символ print (в данном случае нашу функцию).
1 2 3 4 5 |
error = dlerror(); |
Опять проверяем на ошибки.
1 2 |
print(); |
Если все нормально мы наконец можем вызвать нужную нам функцию и закрыть библиотеку.
globfree(&result);
Освобождаем память занятую при универсализации.
Листинг hello_world.c, который и станет нашим плагином:
1 2 3 4 5 |
#include <stdio.h> |
Ну тут думаю все должно быть понятно.
Makefile к main.c:
1 2 |
prog.out : main.c |
Makefile к hello_world.c:
1 2 3 4 5 |
hello_world.so : hello_world.o |
Вот и всё =)