четверг, 5 ноября 2009 г.

C/C++ Модульное программирование в linux: Динамическая загрузка библиотек dlopen



На мой взгляд, первый шаг к модульному программированию - это динамическая загрузка библиотек, конечно помимо других базовых знаний.

Наверняка, все знают чем отличается динамические библиотеки от статических. Но всё же я повторю. Когда вы используете статические библиотеки, их код внедряется в программный код вашей программы на этапе компиляции. Когда же вы используете динамические библиотеки, то их загрузка происходит уже после запуска приложения.

Что нам даст такой подход? А то, что наша программа сможет быть динамически расширяемой. А это даёт неограниченные возможности в масштабировании приложения. К тому же, если приложение большое и тяжелое, это позволит разгрузить его. У модульного программирования много преимуществ, не буду про это писать, а расскажу о своём примере.

Итак, мы создадим 2 библиотеки:
- одна будет содержать обычную функцию, при вызове которой мы будем получать текст в stdoutput;
- другая будет содержать класс, который содержит функцию на подобие той, что описана Выше.
Далее, мы создадим программу, подгружающую эти библиотеки и вызывающую функции из них.




Для начала, создадим файл test.cpp с следующим содержанием:

#include <iostream>

//Наша функция, которую мы будем запускать из нашего приложения.
extern "C" void test() {
    std::cout << "This message from test.so function test" << std::endl;
}
Теперь скомпилируем это всё в библиотеку
g++ -shared -o test.so test.cpp
На выходе получаем test.so Теперь проверим есть ли у нашей библиотеки доступные функции. Воспользуемся командой nm, которая не заменима при работе с библиотеками или объектниками, выполним:
nm test.so
Вывод будет следующего вида:



Большая буква "T" напротив имени функции test, означает что она объявлена в текстовой области. Это как раз то, что нужно нам.

Дальше начнём создавать наш класс TestClass
header:
#ifndef TEST_CLASS_HPP
#define TEST_CLASS_HPP

#include <iostream>
class Test_Class{
public:
 Test_Class(){};
 ~Test_Class(){};
 virtual void test_func();
};
 //Определяем тип класса
 typedef Test_Class* new_class_t();
 typedef void delete_class_t();
#endif
source:
#include "test_class.h"

void Test_Class::test_func() {
       std::cout << "This message from test.so class Test_Class function test" << std::endl;
}

//Внешняя функция создания класса
extern "C" Test_Class* new_class() {
    return new Test_Class;
}
//Внешняя функция удаления класса
extern "C" void delete_class(Test_Class* temp) {
    delete temp;
}
Скомпилируем:
g++ -shared -o test_class.so test_class.cpp

Так, вроде всё готово, осталось создать саму программу, которая будет всем этим пользоваться.
main.cpp:

#include <iostream>
#include <dlfcn.h>
#include "test_class.h"

int open_function()
{
 //Открываем динамическую библиотеку
     void* handle = dlopen("./test.so", RTLD_LAZY);
     //Проверяем на ошибки
     if (!handle) {
         std::cerr <<  dlerror() << std::endl;;
         return 1;
     }
 //Определяем тип  
 typedef void (*test_t)();
  //Загружаем нужную нам функцию.
 test_t test = (test_t) dlsym(handle, "test");
 //Проверяем на ошибки
 if (dlerror()) {
         std::cerr <<  dlerror() << std::endl;
         dlclose(handle);
         return 1;
 }
 
     //Исполняем функцию из библиотеки
 test();
 
 //Закрываем библиотеку
 dlclose(handle);
 return 0;
}

int open_class_function()
{
 //Открываем динамическую библиотеку с классом
    void *handle_c = dlopen("./test_class.so", RTLD_LAZY);
 //Проверяем на ошибки
     if (!handle_c) {
         std::cerr <<  dlerror() << std::endl;;
         return 1;
     }
  
     //Загружаем фукнции по созданию класса.
     new_class_t* new_class = (new_class_t*) dlsym(handle_c, "new_class");
     delete_class_t* delete_class = (delete_class_t*) dlsym(handle_c, "delete_class");
     //Проверяем на ошибки
     if (!new_class || !delete_class) {
         std::cerr <<  dlerror() << std::endl;
         return 1;
     }

     //Создаём класс
     Test_Class* test_class = new_class();

     //Исполняем функцию из класса
    test_class->test_func();
    

     //Удаляем класс
     delete_class();
     //Закрываем библиотеку
 dlclose(handle_c);
     //Выходим
}
int main() {
     
 if(open_function() == 1) std::cout << "open_function missed" << std::endl;
 if(open_class_function()==1) std::cout << "open_class_function missed" << std::endl;
 
     return 0;
}
Компилируем
g++ -o dlopen_example  main.cpp -ldl 
Всё осталось только запустить и убедиться, что всё действительно работает!


Удачного Вам освоения dlopen!

sources

Комментариев нет:

Отправить комментарий