Сегодня будем писать кросс-платформенную динамическую библиотеку (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
Для сборки в 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;
}
Для сборки в 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;
}
Заведем заголовочный файл, в котором будут макросы для экспортирования функций в библиотеку в зависимости от платформы.
Файл 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.
Теперь создадим пользователя этой библиотеки. Первый вариант: У нас есть заголовочный файл и мы будем линковаться с ней при сборке.
Файл 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 и другие функции для работы с динамическими библиотеками.
Комментариев нет:
Отправить комментарий