# Создание библиотек программирования

Создание библиотек программирования в общем напоминает создание обычных приложений, однако есть определённые тонкости.

![Visual Studio: разработка библиотек похожа на разработку обысн](https://2920667026-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LB0_5rBsDd_3XA_RKuq%2Fuploads%2Fgit-blob-6e26a076d6c1622b3b3aedd8b4ba612ed6df3f68%2Fimage.png?alt=media)

В заголовочном файле библиотеки описываются ресурсы, предназначенные для экспорта (то есть для использования программами). Эти ресурсы называются **интерфейсом библиотеки** (или **программным интерфейсом библиотеки** – **API** от Application Programming Interface). Пример заголовочного файла статической библиотеки, описывающей работу с комплексными числами, представлен на листинге 1. Как видно, это обычное объявление класса.

**Листинг 1. Заголовочный файл библиотеки комплексных чисел.**

{% code title="complex.h" %}

```cpp
#ifndef COMPLEX_H
#define COMPLEX_H

#include <iostream>
#include <string>

class complex{
    double _real, _imaginary;
public:
    complex(const double& = 0, const double& = 0);
    complex(const complex&);
    ~complex();
    
    void operator +=(const complex&);
    void operator -=(const complex&);
    void operator *=(const complex&);
    void operator /=(const complex&);
    
    complex& operator = (const complex&);
    complex& operator = (const double&);
    
    const double& real() const;
    const double& imaginary() const;
    void swap(complex&);
    std::string toString() const;
    double module() const;
    complex conjugate() const;
};

complex operator + (const complex&, const complex&);
complex operator - (const complex&, const complex&);
complex operator * (const complex&, const complex&);
complex operator / (const complex&, const complex&);

std::ostream& operator << (std::ostream&, const complex&);
bool operator  == (const complex&, const complex&);

complex pow(const complex&, const complex&);
double abs(const complex&);

#endif	/* COMPLEX_H */
```

{% endcode %}

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

```cpp
#ifndef CONSTANT_NAME
#define CONSTANT_NAME

//…

#endif
```

Эти директивы на этапе препроцессирования программного кода позволяют включать определения ресурсов только один раз.

*Компиляторы фирмы Microsoft (и Intel) позволяют использовать вместо конструкции ветвления препроцессора конструкцию*

```cpp
#pragma once
```

Что является предписанием компилятору, что данный файл подключается только раз.

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

**Листинг 2. Реализация класса комплексных чисел.**

{% code title="complex.cpp" %}

```cpp
#include "complex.h"
#include <sstream>

complex::complex(const double& r, const double& im): _real(r), _imaginary(im){}
complex::complex(const complex& c): _real(c._real), _imaginary(c._imaginary){}
complex::~complex(){}

void complex::operator +=(const complex& c){
    _real += c._real;
    _imaginary += c._imaginary;
}
void complex::operator -=(const complex& c){
    _real -= c._real;
    _imaginary -= c._imaginary;
}
void complex::operator *=(const complex& c){
    complex tmp;
    tmp._real = _real*c._real - _imaginary*c._imaginary;
    tmp._imaginary = _real*c._imaginary + _imaginary*c._real;
    swap(tmp);
}
void complex::operator /=(const complex& c){
    complex tmp;
    tmp = (*this) * c.conjugate();
    _real = tmp._real / c.module();
    _imaginary = tmp._imaginary / c.module();
}

complex& complex::operator =(const complex& c){
    _real = c._real;
    _imaginary = c._imaginary;
    return *this;
}
complex& complex::operator =(const double& d){
    _real = d;
    _imaginary = 0;
    return *this;
}

const double& complex::real() const { return _real; }
const double& complex::imaginary() const { return _imaginary; }
void complex::swap(complex& c){
    complex tmp(c);
    c._imaginary = _imaginary;
    c._real = _real;
    _imaginary = tmp._imaginary;
    _real = tmp._real;
}
std::string complex::toString() const{
    std::ostringstream strcout;
    strcout << _real << " + i*( " << _imaginary << " )";
    return strcout.str();
}
double complex::module() const{
    return _real*_real + _imaginary*_imaginary;
}
complex complex::conjugate() const{
    return complex(_real, -_imaginary);
}

// extern class interface
complex operator + (const complex& c1, const complex& c2){
    complex tmp(c1);
    tmp += c2;
    return tmp;
}
complex operator - (const complex& c1, const complex& c2){
    complex tmp(c1);
    tmp += c2;
    return tmp;
}
complex operator * (const complex& c1, const complex& c2){
    complex tmp(c1);
    tmp += c2;
    return tmp;
}
complex operator / (const complex& c1, const complex& c2){
    complex tmp(c1);
    tmp += c2;
    return tmp;
}

std::ostream& operator << (std::ostream& os, const complex& c){
    os << c.toString();
    return os;
}
bool operator  == (const complex& c1, const complex& c2){
    return ((c1.real() == c2.real()) && (c1.imaginary() == c2.imaginary()));
}

complex pow(const complex&, const complex&){
    complex result;
    return result;
}
double abs(const complex& c){
    return c.module();
}
```

{% endcode %}

## Особенности создания динамических библиотек

В случае разработки динамических библиотек, их создание может происходить аналогично созданию статических библиотек. Особенностью, в данном случае, будет только выбор типа проекта. В этом случае библиотека может использоваться неявно (то есть, через подключение заголовочного файла и указание использования файла импорта библиотеки). Для использования функций библиотеки необходимо явно указать, к каким функциям может получить доступ зависимая от данной библиотеки программа. Для этого используется специальная конструкция языка ***\_\_declspec(dllexport)***, указывающая, что функция (класс) доступна для использования. В листинге 3 представлен пример заголовочного файла, в котором описан класс комплексного числа. Этот класс предназначен на экспорт.

**Листинг 3. Экспорт классов и функций.**

{% code title="complex\_dll.h" %}

```cpp
#pragma once
#include <iostream>
#include <string>

#define DLLEXPORT __declspec(dllexport)

class DLLEXPORT complex
{
    double _real, _imaginary;
public:
    complex(const double = 0, const double = 0);
    complex(const complex&);
    ~complex(void);

    complex& operator = (const complex&);
    const double& real() const;
    const double& imaginary() const;
    const std::string toString() const;

    void operator += (const complex&);
};

DLLEXPORT bool operator == (const complex&, const complex&);
DLLEXPORT complex operator + (const complex&, const complex&);
DLLEXPORT std::ostream& operator << (std::ostream&, const complex&);
DLLEXPORT int min(int, int);
```

{% endcode %}

Также функции на экспорт можно определить при помощи def-файла (файл определения модуля), что позволяет не использовать конструкцию \_\_declspec(dllexport). Файл определения модуля описывается по следующим правилам:

1. Первая строка файла есть указание имени библиотеки, при помощи инструкции *LIBRARY*.
2. Вторая секция открывается инструкцией *EXPORTS*, она указывает, какие части библиотеки идут на экспорт. Каждая следующая строка представляет имя функции и порядковый номер в библиотеке, выделенный символом @.

{% code title="extmath.h" %}

```cpp
#ifndef EXTMATH_H
#define EXTMATH_H

size_t gcd (size_t, size_t);
bool is_prime(size_t);
bool is_odd(size_t);

#endif
```

{% endcode %}

{% code title="extmath.cpp" %}

```cpp
#include "extmath.cpp"

size_t gcd (size_t a, size_t b){
    if(a == 0){
        return b;
    }
    while(b != 0){
        size_t tmp = a % b;
        a = b;
        b = tmp;
    }
    return a;
}
bool is_prime(size_t p){
    size_t i = 2;
    size_t top = p / 2;
    while(i <= top){
        if(p / i == 0){
            return false;
        }
        ++ i;
    }
    return true;
}
bool is_odd(size_t p){
    return (p % 2 == 0);
}
```

{% endcode %}

**Листинг 4. Пример def-файла**

```
LIBRARY  EXTMATH
EXPORTS
            gcd          @1
            is_prime     @2
            is_odd       @3
```

Описанный в листингах 2 и 3, класс можно использовать только в С++ проектах. Для создания библиотек, совместимых с языком С, необходимо заголовочные файлы писать в стиле С (без использования классов, шаблонов и др. специфичные для С++ конструкции). Также желательно в макросе DLLEXPORT приписать модификатор **extern ”C”** – это позволяет, при компиляции библиотеки, сохранить оригинальные имена экспортируемых ресурсов (некоторые компиляторы «кодируют» имена функций и классов).

Использование динамической библиотеки, созданной таким образом, не будет возможно в других, отличных от С/С++, языках программирования. Это связанно с тем, что в языках Pascal, Basic и др. функции читают входные параметры слева направо, тогда как в С/С++ входные параметры читаются справа налево. Для создания совместимой с этой группой языков библиотеки используется модификатор ***\_\_stdcall*** (или эквивалент ***\_\_pascal***), который меняет порядок чтения входных параметров функции.

> В OS Windows динамические библиотеки могут иметь также точку входа - функцию DllMain:
>
> ```cpp
> #include <windows.h>
>
> BOOL APIENTRY DllMain( HMODULE hModule,
>                        DWORD  ul_reason_for_call,
>                        LPVOID lpReserved
>  ){
>     switch (ul_reason_for_call){
>         case DLL_PROCESS_ATTACH:
>         case DLL_THREAD_ATTACH:
>         case DLL_THREAD_DETACH:
>         case DLL_PROCESS_DETACH:
>             break;
>     }
>     return TRUE;
> }
> ```

## Сборка библиотек

Самый простой способ создания библиотек - выбор соответстувующего проекта в среде программирования. Все современные среды программирования предлагают проекты как статических, так и динамических библиотек.

Разработка статических библиотек ничем не отличается от написания программы, только в статических библиотеках нет функции *main*.
