среда, 10 апреля 2013 г.

Shared library

Сегодня будем писать кросс-платформенную динамическую библиотеку (DLL).

Заведем заголовочный файл, в котором будут макросы для экспортирования функций в библиотеку в зависимости от платформы.

Файл export.h:

#ifndef _EXPORT_H
#define _EXPORT_H


#if defined (_MSC_VER)
#ifdef __cplusplus
#define LIB_EXPORT extern "C" __declspec(dllexport)
#else
#define LIB_EXPORT __declspec(dllexport)
#endif
#elif defined (__GNUC__)
#ifdef __cplusplus
#define LIB_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define LIB_EXPORT __attribute__((visibility("default")))
#endif
#else
#warning Unknown dynamic link semantics
#ifdef __cplusplus
#define LIB_EXPORT extern "C" 
#else
#define LIB_EXPORT 
#endif
#endif


#endif


Теперь создадим саму библиотеку. Покажем, как делать экспорт функций и классов.

Файл export_functions.h:

#ifndef EXPORT_FUNCTIONS_H
#define EXPORT_FUNCTIONS_H


#include "export.h"


LIB_EXPORT int export_function(int x);


class EClass
{
public:
virtual void func() =0;
};


class EClassImpl : public EClass
{
public:
virtual void func();
};


LIB_EXPORT EClass *GetEClass();
LIB_EXPORT void DeleteEClass(EClass *a);


#endif

Файл export_functions.cpp:
#include <iostream>

#include "export_functions.h"

using namespace std;

int export_function(int x)
{
cout << "DLL function! parameter: " << x << endl;
return x;
}

EClass *GetEClass()
{
return new EClassImpl;
}

void DeleteEClass(EClass *a)
{
delete a;
}

void EClassImpl::func()
{
cout << "Class function from DLL!" << endl;
}

Положим все эти файлы в папку SharedLibrary.


Для сборки под Linux при помощи GCC можно использовать следующий Makefile:
libshared:
g++ -fPIC -Wall -g -c export_functions.cpp
g++ -shared -Wl,-soname, -o libshared.so export_functions.o -lc


clean: 
rm -f *.o libshared.so 

Файл libshared.so требуется либо положить в стандартное место для библиотек, либо перед использованием изменять переменную LD_LIBRARY_PATH.

Для сборки в Visual Studio под Windows требуется создать проект типа Win32 Console application и указать, что это DLL.

Теперь создадим пользователя этой библиотеки. Первый вариант: У нас есть заголовочный файл и мы будем линковаться с ней при сборке.

Файл StaticUser.cpp:

#include <iostream>


#include "../SharedLibrary/export_functions.h"


int main(int argc, char **argv)
{
export_function(5);


EClass *a = GetEClass();
a->func();
DeleteEClass(a);
return 0;
}

Makefile для сборки под Linux:
StaticUser: 
g++ -Wall -g -c StaticUser.cpp 
g++ -g -o StaticUser StaticUser.o -L../SharedLibrary -lshared 


Для сборки в Visual Studio под Windows в зависимостях проект (Project dependencies) требуется поставить проект SharedLibrary.

Второй вариант: У нас нет заголовочного файла, и мы будем загружать библиотеку самостоятельно.

В Windows загрузка DLL осуществляется при помощи функции LoadLibrary. Если загрузка DLL произошла удачно, возвращается переменная типа HINSTANCE (В VisualStudio этот тип определен как void *). В противном случае возвращается 0. Чтобы понять, какая произошла ошибка, вызываем функцию GetLastError. Коды ошибок в Windows можно посмотреть тут.

Пользователь библиотеки будет состоять из одного файла LibraryUser.cpp:

#include <iostream>
#include <Windows.h>


using namespace std;


typedef int (*export_function_t) (int);
export_function_t export_function;


int main(int argc, char **argv)
{
DWORD err;
LPCSTR path = (LPCSTR)"SharedLibrary.dll";


if (GetFileAttributesA(path) == -1) //file does not exist.
{
cout << "Library not found ):" << endl;
}


HINSTANCE hDLL = LoadLibraryA(path);


if (hDLL)
{
cout << "Library loaded!" << endl;


export_function = reinterpret_cast<export_function_t> (GetProcAddress(hDLL, "export_function"));
if (!export_function)
{
cout << "Function not loaded ):" << endl;
return 1;
}


cout << "Function export_function loaded!" << endl;
int x = export_function(5);

cout << "Function returned " << x << endl;
}
else
{
err = GetLastError();
cout << "Error in load library: " << err << endl;
}
return 0;
}

Под Linux загрузка библиотеки осуществляется функцией dlopen.
Линуксовый пользователь библиотеки будет состоять из файла lu.cpp:

#include <iostream>
#include <dlfcn.h>
#include <link.h>


#include "../SharedLibrary/export_functions.h"


using namespace std;


int main(int argc, char ** argv)
{
void *handle = dlopen("/lib/libshared.so", RTLD_LAZY);


if (!handle)
{
cerr << "Couldn't load library ):" << endl;
return 1;
}


EClass * (*GetEClass)();
void * (DeleteEClass)(EClass *);


GetEClass = (EClass* (*)())dlsym(handle, "GetEClass");


EClass *a = (EClass *) GetEClass();
a->func();


return 0;
}

Собирается файл строкой g++ -o lu -ldl lu.cpp. Ключ -ldl подключает библиотеку, в которой находятся dlopen и другие функции для работы с динамическими библиотеками.



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

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