|  |  |  | @ -0,0 +1,814 @@ | 
			
		
	
		
			
				
					|  |  |  |  | # Использование библиотек | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Цель работы | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 1. Уметь устанавливать и подключать к программе внешние библиотеки. | 
			
		
	
		
			
				
					|  |  |  |  | 1. Уметь использовать типовые элементы API библиотек. | 
			
		
	
		
			
				
					|  |  |  |  | 1. Уметь работать с параметрами командной строки программы. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Задание | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Добавить возможность построения гистограммы по данным из файла в сети. | 
			
		
	
		
			
				
					|  |  |  |  | Адрес файла задается аргументом командной строки программы. | 
			
		
	
		
			
				
					|  |  |  |  | Если адрес не задан, читать данные со стандартного ввода, как раньше. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Пример запуска программы с загрузкой данных из файла на сайте кафедры: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | lab34.exe http://uit.mpei.ru/study/courses/cs/lab03/marks.txt >marks.svg | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Работу нужно вести на основе кода общего задания к ЛР № 3 в прежнем репозитарии. | 
			
		
	
		
			
				
					|  |  |  |  | По этой причине во всех примерах используется `lab34.exe`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Защита представляет собой демонстрацию выполненного индивидуального задания, | 
			
		
	
		
			
				
					|  |  |  |  | ответы на вопросы по коду и по релевантной теории (см. контрольные вопросы). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Указания | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Скачивать файл по сети будем с помощью библиотеки [cURL](https://curl.haxx.se). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Получать сведения о системе будем с помощью API операционной системы. | 
			
		
	
		
			
				
					|  |  |  |  | Это тоже внешняя библиотека, но её не требуется устанавливать. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ### Ввод из произвольного потока | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Рефакторинг (refactoring) — это изменение кода программы | 
			
		
	
		
			
				
					|  |  |  |  | без изменения её функциональных характеристик | 
			
		
	
		
			
				
					|  |  |  |  | (интересующего пользователя поведения). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Зачем менять код, если это не добавляет возможностей и не исправляет ошибок? | 
			
		
	
		
			
				
					|  |  |  |  | Например, для того, чтобы облегчить дальнейшее внесение изменений в него. | 
			
		
	
		
			
				
					|  |  |  |  | В нашем случае программа жестко завязана на то, | 
			
		
	
		
			
				
					|  |  |  |  | что данные поступают со стандартного ввода. | 
			
		
	
		
			
				
					|  |  |  |  | Ожидается, что они смогут поступать не только оттуда. | 
			
		
	
		
			
				
					|  |  |  |  | Код нужно переписать так, чтобы он мог работать с любым входящим потоком. | 
			
		
	
		
			
				
					|  |  |  |  | Тогда текущее поведение будет частным случаем нового, | 
			
		
	
		
			
				
					|  |  |  |  | когда поток — стандартный ввод (`cin`). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Что такое «работать с потоком ввода»? | 
			
		
	
		
			
				
					|  |  |  |  | Рассмотрим любой пример работы со стандартным потоком ввода: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | cin >> number_count; | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Здесь есть переменная `cin`, которая представляет поток, и операция `>>` с ней. | 
			
		
	
		
			
				
					|  |  |  |  | Работать с другим потоком значит использовать иную переменную вместо `cin`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Что такое «работать с любым потоком», если поток — это переменная в программе? | 
			
		
	
		
			
				
					|  |  |  |  | Это значит, в программе есть блок кода для ввода данных, | 
			
		
	
		
			
				
					|  |  |  |  | который принимает переменную-поток и работает с ней. | 
			
		
	
		
			
				
					|  |  |  |  | Но параметризованный блок кода — это же функция! | 
			
		
	
		
			
				
					|  |  |  |  | Вводом данных занимается функция `input_data()`. | 
			
		
	
		
			
				
					|  |  |  |  | Следовательно, у нее должен появится параметр — поток, из которого читать. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | Input | 
			
		
	
		
			
				
					|  |  |  |  | input_data(istream& in) { | 
			
		
	
		
			
				
					|  |  |  |  |     // Прежний код, но работающий с in вместо cin. | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Это должна быть именно ссылка, потому что нельзя скопировать поток ввода — | 
			
		
	
		
			
				
					|  |  |  |  | он объективно один во всей программе. | 
			
		
	
		
			
				
					|  |  |  |  | Это не может быть константная ссылка, потому что функция меняет поток: | 
			
		
	
		
			
				
					|  |  |  |  | данные после вычитывания оттуда «исчезают». | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Вызов функции будет выглядеть так: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | auto in = input_data(cin); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Перепишите функцию `input_data()` и ее вызов, как предложено. | 
			
		
	
		
			
				
					|  |  |  |  | Убедитесь, что поведение программы не изменилось. | 
			
		
	
		
			
				
					|  |  |  |  | Сделайте коммит. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте `input_data()` новый параметр: `bool prompt`. | 
			
		
	
		
			
				
					|  |  |  |  | Если он равен `true`, то нужно выводить в `cerr` подсказки, | 
			
		
	
		
			
				
					|  |  |  |  | если `false`, то не нужно их выводить. | 
			
		
	
		
			
				
					|  |  |  |  | Сделайте коммит. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ### Загрузка данных по сети | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Установка cURL | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Страница библиотеки cURL: <https://curl.haxx.se/libcurl/>. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | На этой странице есть раздел *Download* со ссылкой на страницу загрузок: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Вариантов загрузки очень много, потому что cURL собирается под много | 
			
		
	
		
			
				
					|  |  |  |  | операционных систем (Windows, Linux, различные Unix-системы), архитектур | 
			
		
	
		
			
				
					|  |  |  |  | процессора (Intel/AMD, ARM и других) и их вариантов (32 или 64 бита). | 
			
		
	
		
			
				
					|  |  |  |  | Windows находится в конце списка: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Для Windows следует выбрать ссылку с описанием `binary the curl project`: | 
			
		
	
		
			
				
					|  |  |  |  | Странице загрузок для Windows — ссылки, чтобы скачать архив для системы нужной | 
			
		
	
		
			
				
					|  |  |  |  | разрядности. Как выбрать разрядность? Это зависит не от версии Windows, | 
			
		
	
		
			
				
					|  |  |  |  | а от того, собирает компилятор 32-битные или 64-битные программы. По умолчанию | 
			
		
	
		
			
				
					|  |  |  |  | с CodeBlocks идет компилятор 32-битных программ mingw32, он же устанавливается | 
			
		
	
		
			
				
					|  |  |  |  | по инструкции для РПОСУ. Используемый компилятор можно увидеть в *Build log* | 
			
		
	
		
			
				
					|  |  |  |  | при сборке программы. Если там mingw64, значит, компилятор 64-битный. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | В архиве находится папка `curl-7.69.1-win32-mingw` | 
			
		
	
		
			
				
					|  |  |  |  | (версия cURL и разрядность компилятора та, которая выбрана для загрузки). | 
			
		
	
		
			
				
					|  |  |  |  | Ее нужно извлечь, | 
			
		
	
		
			
				
					|  |  |  |  | переименовать в `curl` для удобства | 
			
		
	
		
			
				
					|  |  |  |  | и переместить в каталог проекта. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Занесите эту папку в `.gitignore`. | 
			
		
	
		
			
				
					|  |  |  |  | Проверьте, что сделали это правильно, с помощью команды `git status`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Из папки `curl` далее понадобятся: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | * `bin/libcurl.dll` — динамическая библиотека (`libcurl-x64.dll` для 64 бит); | 
			
		
	
		
			
				
					|  |  |  |  | * `lib` — каталог со статическими библиотеками; | 
			
		
	
		
			
				
					|  |  |  |  | * `include` — каталог с заголовочными файлами. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Примечание.** | 
			
		
	
		
			
				
					|  |  |  |  | Есть инструменты, которые позволяют скачивать и устанавливать библиотеки | 
			
		
	
		
			
				
					|  |  |  |  | из командной строки, для Windows это *Chocolatey* и *vcpkg.* | 
			
		
	
		
			
				
					|  |  |  |  | Они автоматизируют и стандартизируют то, | 
			
		
	
		
			
				
					|  |  |  |  | что в рамках этой ЛР делается вручную, чтобы разобраться. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Подключение cURL к проекту | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Каждая программа должна до первого вызова функций библиотеки cURL вызвать | 
			
		
	
		
			
				
					|  |  |  |  | функцию [`curl_global_init()`][curl_global_init]. | 
			
		
	
		
			
				
					|  |  |  |  | Например, это можно сделать первой строкой в `main()`: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | curl_global_init(CURL_GLOBAL_ALL); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | При попытке собрать программу возникнут ошибки, текст которых означает, | 
			
		
	
		
			
				
					|  |  |  |  | что константа `CURL_GLOBAL_ALL` и функция `curl_global_init()` не объявлены, | 
			
		
	
		
			
				
					|  |  |  |  | то есть неизвестны компилятору: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | error: 'CURL_GLOBAL_ALL' was not declared in this scope | 
			
		
	
		
			
				
					|  |  |  |  | error: 'curl_global_init' was not declared in this scope | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Если открыть документацию по ссылке, она начинается так: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | > SYNOPSIS | 
			
		
	
		
			
				
					|  |  |  |  | > | 
			
		
	
		
			
				
					|  |  |  |  | > #include <curl/curl.h> | 
			
		
	
		
			
				
					|  |  |  |  | > | 
			
		
	
		
			
				
					|  |  |  |  | > CURLcode curl_global_init(long flags); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | «Synopsis» значит «сводка» (отсюда слово «синоптик»), это типовой раздел | 
			
		
	
		
			
				
					|  |  |  |  | справки к библиотекам, в котором кратко показывается, как функция объявлена | 
			
		
	
		
			
				
					|  |  |  |  | и какой заголовочный нужно подключить, чтобы ее использовать. В данном случае | 
			
		
	
		
			
				
					|  |  |  |  | нужно добавить в начало `main.cpp`: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | #include <curl/curl.h> | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | При компиляции все равно будет ошибка, но другая — что файл не найден: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | fatal error: curl/curl.h: No such file or directory | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Как известно из лекций, `#include <...>` ищет файл в неких «стандартных | 
			
		
	
		
			
				
					|  |  |  |  | каталогах», куда папка `include` из архива не входит. Чтобы добавить её | 
			
		
	
		
			
				
					|  |  |  |  | в список, нужно открыть диалог *Project → Build options...* и перейти | 
			
		
	
		
			
				
					|  |  |  |  | во вкладку *Search directories.*  Там три дочерних вкладки: *Compile* | 
			
		
	
		
			
				
					|  |  |  |  | (пути для поиска `#include <...>`), *Linker* (пути для поиска библиотек) | 
			
		
	
		
			
				
					|  |  |  |  | и *Resource compiler* (не требуется). Нужно нажать кнопку *Add,* в открывшемся | 
			
		
	
		
			
				
					|  |  |  |  | диалоге указать `curl/include` и нажать *OK* (путь к папке можно также | 
			
		
	
		
			
				
					|  |  |  |  | выбрать через диалог рядом с полем ввода). Каталог `curl/include` добавится | 
			
		
	
		
			
				
					|  |  |  |  | в список, диалог можно закрыть кнопкой *OK.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Внимание.** | 
			
		
	
		
			
				
					|  |  |  |  | Рекомендуется вводить пути и имена файлов в настройках как текст, | 
			
		
	
		
			
				
					|  |  |  |  | а не выбирать их с помощью диалогов. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Ошибка сборки изменится: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | undefined reference to `__imp_curl_global_init' | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Обратите внимание, что это уже ошибка не компиляции, а компоновки (линкера). | 
			
		
	
		
			
				
					|  |  |  |  | Исходный текст программы правильный, все заголовочные файлы найдены, | 
			
		
	
		
			
				
					|  |  |  |  | появился `obj/Debug/main.o`. Но чтобы создать из него исполняемый файл | 
			
		
	
		
			
				
					|  |  |  |  | не хватает машинного кода функции `curl_global_init()`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Чтобы подключить библиотеку, нужно два действия: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 1. Вернуться к *Search directories,* теперь на дочернюю вкладку *Linker.* | 
			
		
	
		
			
				
					|  |  |  |  |     Там нужно добавить каталог `curl/lib` аналогично предыдущему шагу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 1. Указать, какую именно библиотеку подключить. Если просмотреть каталог | 
			
		
	
		
			
				
					|  |  |  |  |     `curl/lib`, там есть `libcurl.a` и `libcurl.dll.a`. Сейчас выберем | 
			
		
	
		
			
				
					|  |  |  |  |     `libcurl.dll.a`, разница будет пояснена в последующих пунктах. | 
			
		
	
		
			
				
					|  |  |  |  |     На вкладке *Linker settings* (соседняя с *Search directories)* нужно | 
			
		
	
		
			
				
					|  |  |  |  |     нажать *Add* и ввести `libcurl.dll.a`, после чего сохранить настройки | 
			
		
	
		
			
				
					|  |  |  |  |     и покинуть диалог нажатиями *OK.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Если все сделано правильно, сборка должна проходить успешно. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | [curl_global_init]: https://curl.haxx.se/libcurl/c/curl_global_init.html | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Установка динамических библиотек | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Перейдите в каталог `bin/Debug`, откройте там консоль и запустите программу: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cmd | 
			
		
	
		
			
				
					|  |  |  |  | lab34.exe | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Будет ошибка (главное в ней — имя библиотеки `libcurl….dll`): | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Дело в том, что программа собрана с использованием *динамической* библиотеки. | 
			
		
	
		
			
				
					|  |  |  |  | В `lab34.exe` нет кода cURL, а только указание загрузить `libcurl-x64.dll`, | 
			
		
	
		
			
				
					|  |  |  |  | где этот код есть, и вызвать его. Но библиотеки нет в тех каталогах, | 
			
		
	
		
			
				
					|  |  |  |  | где Windows ищет библиотеки. Одним из таких каталогов является текущий. | 
			
		
	
		
			
				
					|  |  |  |  | Скопируйте `libcurl-x64.dll` в `bin/Debug` и попробуйте запустить программу | 
			
		
	
		
			
				
					|  |  |  |  | (`libcurl.dll` для 32 бит). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Убедитесь, что программа запускается. Сделайте коммит. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | --- | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Врезка отсюда до горизонтальной черты не нужна для новых версий cURL. | 
			
		
	
		
			
				
					|  |  |  |  | Если после размещения `libcurl….dll` в `bin/Debug` программа запускается, | 
			
		
	
		
			
				
					|  |  |  |  | пропустите это дополнение.** | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Ошибка изменится, теперь не найдены будут `libcrypto-1_1.dll` | 
			
		
	
		
			
				
					|  |  |  |  | и `libssl-1_1.dll`, но их уже нет в поставке cURL. В интернете можно узнать, | 
			
		
	
		
			
				
					|  |  |  |  | что эти библиотеки из набора OpenSSL, нужного cURL для установки защищенных | 
			
		
	
		
			
				
					|  |  |  |  | соединений. Откуда их взять? | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Разумеется, в интернете найдется всё: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Никогда не устанавливайте библиотеки из непроверенных источников!** | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Чтобы установить недостающие библиотеки, нужно посетить официальный сайт, | 
			
		
	
		
			
				
					|  |  |  |  | как правило, там есть ссылки для загрузки, и им можно доверять. Кроме того, | 
			
		
	
		
			
				
					|  |  |  |  | даже если скачанная библиотека будет подлинной (безопасной), раскладывать | 
			
		
	
		
			
				
					|  |  |  |  | отдельные библиотеки вручную ненадежно. Через OpenSSL wiki можно найти | 
			
		
	
		
			
				
					|  |  |  |  | [сайт с готовыми сборками](https://slproweb.com/products/Win32OpenSSL.html). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Скачайте установочный пакет (MSI) по ссылке в первой или третьей строке | 
			
		
	
		
			
				
					|  |  |  |  | в зависимости от разрядности системы: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Внимание.** | 
			
		
	
		
			
				
					|  |  |  |  | Разрядность cURL и OpenSSL должны совпадать. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | В процессе установки: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |  | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | * При выборе *Copy OpenSSL DLLs to:* нужно выбрать *The Windows system | 
			
		
	
		
			
				
					|  |  |  |  |     directory,* чтобы библиотеки были доступны при запуске программы | 
			
		
	
		
			
				
					|  |  |  |  |     из любого каталога. | 
			
		
	
		
			
				
					|  |  |  |  | * На последнем экране автор сборки предлагает поддержать его труд 10 \$. Можно | 
			
		
	
		
			
				
					|  |  |  |  |     просто снять флажок и продолжить. OpenSSL распространяется бесплатно. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | --- | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Статическая сборка программы | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Попробуем собрать программу статически. Чтобы поменять компоновку cURL | 
			
		
	
		
			
				
					|  |  |  |  | на статическую, необходимо: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 1. Вернуться в *Linker settings,* выбрать в списке `libcurl.dll.a`, | 
			
		
	
		
			
				
					|  |  |  |  |     нажать *Edit,* изменить имя на `libcurl.a` и нажать *OK.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 2. Перейти на вкладку *Compiler settings* (рядом с *Linker settings),* | 
			
		
	
		
			
				
					|  |  |  |  |     выбрать дочернюю вкладку *#defines.* | 
			
		
	
		
			
				
					|  |  |  |  |     Там в текстовое поле нужно ввести `CURL_STATICLIB` | 
			
		
	
		
			
				
					|  |  |  |  |     и выйти из диалога кнопкой *OK.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Примечание.** | 
			
		
	
		
			
				
					|  |  |  |  | Последний шаг специфичен для cURL, аналогичный шаг для других библиотек | 
			
		
	
		
			
				
					|  |  |  |  | обычно требуется только на Windows. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | При сборке будет множество ошибок: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | curl\lib\libcurl.a(connect.o):(.text+0x1d1): undefined reference to `__imp_ntohs' | 
			
		
	
		
			
				
					|  |  |  |  | curl\lib\libcurl.a(connect.o):(.text+0x2ee): undefined reference to `__imp_getpeername' | 
			
		
	
		
			
				
					|  |  |  |  | curl\lib\libcurl.a(connect.o):(.text+0x2f8): undefined reference to `__imp_WSAGetLastError' | 
			
		
	
		
			
				
					|  |  |  |  | ... | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Причина в том, что код cURL использует функции из других библиотек, | 
			
		
	
		
			
				
					|  |  |  |  | поэтому программа должна линковаться с ними всеми. | 
			
		
	
		
			
				
					|  |  |  |  | Для этого нужно определять, | 
			
		
	
		
			
				
					|  |  |  |  | из какой библиотеки недостающая функция (обычно поиском в интернете), | 
			
		
	
		
			
				
					|  |  |  |  | устанавливать эту библиотеку (если она не системная), | 
			
		
	
		
			
				
					|  |  |  |  | добавлять её в *Linker settings* и пробовать собрать снова. | 
			
		
	
		
			
				
					|  |  |  |  | В рамках ЛР этого делать не нужно. | 
			
		
	
		
			
				
					|  |  |  |  | На практике у зависимостей основной библиотеки могут быть другие лицензии, | 
			
		
	
		
			
				
					|  |  |  |  | которые придется принимать во внимание. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Удалите все изменения, сделанные в попытках собрать программу статически, | 
			
		
	
		
			
				
					|  |  |  |  | при помощи git. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Получение аргументов программы | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Из ЛР № 1 известно, что программа может запускаться с аргументами, | 
			
		
	
		
			
				
					|  |  |  |  | которые перечисляются после ее имени через пробел: | 
			
		
	
		
			
				
					|  |  |  |  | `fc /N expected.txt actual.txt`. | 
			
		
	
		
			
				
					|  |  |  |  | Здесь `fc` (`fc.exe`) — исполняемый файл программы, | 
			
		
	
		
			
				
					|  |  |  |  | а `/N`, `expected.txt` и `actual.txt` — три аргумента. | 
			
		
	
		
			
				
					|  |  |  |  | Перенаправление ввода-вывода не является аргументом, например, | 
			
		
	
		
			
				
					|  |  |  |  | команда `lab01.exe <input.txt` вызывает `lab01.exe` без аргументов. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Получить значения аргументов позволяет следующая версия функции `main()`: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | int | 
			
		
	
		
			
				
					|  |  |  |  | main(int argc, char* argv[]) { | 
			
		
	
		
			
				
					|  |  |  |  |     ... | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Примечание.** | 
			
		
	
		
			
				
					|  |  |  |  | В некоторых источниках можно найти `int main(int argc, char** argv)` — | 
			
		
	
		
			
				
					|  |  |  |  | это то же самое. | 
			
		
	
		
			
				
					|  |  |  |  | Все формы `void main(...)` являются нестандартными. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Параметр `argc` (**arg**ument **c**ount) показывает количество элементов | 
			
		
	
		
			
				
					|  |  |  |  | в массиве `argv` (**arg**ument **v**alues). | 
			
		
	
		
			
				
					|  |  |  |  | Начальный элемент (с индексом 0) содержит имя программы, | 
			
		
	
		
			
				
					|  |  |  |  | то есть при запуске без параметров `argc == 1`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Напишите в начале `main()` код, который в случае, если `argc` больше 1, | 
			
		
	
		
			
				
					|  |  |  |  | распечатывает `argc` и `argv` в формате `argv[0] = ...` и выходит (`return 0`). | 
			
		
	
		
			
				
					|  |  |  |  | Сделайте коммит. Проверьте на таких примерах, поясните результат: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | * `lab34.exe -x --y /z w` | 
			
		
	
		
			
				
					|  |  |  |  | * `lab34.exe param "with spaces"` | 
			
		
	
		
			
				
					|  |  |  |  | * `lab34.exe param <marks.txt >NUL` (скачайте `marks.txt`) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Примечание.** | 
			
		
	
		
			
				
					|  |  |  |  | Необязательные аргументы, | 
			
		
	
		
			
				
					|  |  |  |  | которые имеют фиксированные имена (например, `/N` у `fc`), | 
			
		
	
		
			
				
					|  |  |  |  | иногда называют опциями. | 
			
		
	
		
			
				
					|  |  |  |  | Имена опций принято начинать с `-`, `--` или `/` (только в Windows). | 
			
		
	
		
			
				
					|  |  |  |  | Остальные аргументы называют *позиционными.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Загрузка файла по сети | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Весь код в этом пункте должен выполняться только в случае `argc > 1`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Для простых сценариев cURL имеет набор easy-функций, которые позволяют | 
			
		
	
		
			
				
					|  |  |  |  | скачать файл всего в несколько строк. Первой должна быть вызвана | 
			
		
	
		
			
				
					|  |  |  |  | [`curl_easy_init()`][curl_easy_init]: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | [curl_easy_init]: https://curl.haxx.se/libcurl/c/curl_easy_init.html | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | CURL* curl = curl_easy_init(); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Что за тип данных `CURL*`? Это так называемый *дескриптор (handle).* | 
			
		
	
		
			
				
					|  |  |  |  | Очень часто библиотека оперирует сложными объектами, | 
			
		
	
		
			
				
					|  |  |  |  | внутреннее устройство которых программе знать не нужно, | 
			
		
	
		
			
				
					|  |  |  |  | но с другой стороны, программа должна сообщать функциям библиотеки, | 
			
		
	
		
			
				
					|  |  |  |  | с каким объектом работать. | 
			
		
	
		
			
				
					|  |  |  |  | В случае cURL таким объектом выступает сетевое соединение и связанные данные. | 
			
		
	
		
			
				
					|  |  |  |  | Единственное, что можно делать с дескрипторами — | 
			
		
	
		
			
				
					|  |  |  |  | передавать их функциям библиотеки и сравнивать между собой. | 
			
		
	
		
			
				
					|  |  |  |  | Переменную типа `CURL*` нельзя разыменовать, хотя это и указатель. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Как правило, если есть функция создания дескриптора, как `curl_easy_init()`, | 
			
		
	
		
			
				
					|  |  |  |  | есть и функция его освобождения (закрытия). | 
			
		
	
		
			
				
					|  |  |  |  | Действительно, документация гласит: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | > This call MUST have a corresponding call to curl_easy_cleanup when the | 
			
		
	
		
			
				
					|  |  |  |  | > operation is complete. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Это означает, что вся работа с `curl` должна делаться | 
			
		
	
		
			
				
					|  |  |  |  | между написанной строкой и следующим вызовом: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | curl_easy_cleanup(curl); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Изучите пример (раздел *EXAMPLE*) документации на `curl_easy_init()`. | 
			
		
	
		
			
				
					|  |  |  |  | Перенесите пример в программу, но вместо фиксированного адреса | 
			
		
	
		
			
				
					|  |  |  |  | используйте первый аргумент командной строки (с индексом 1). | 
			
		
	
		
			
				
					|  |  |  |  | Убедитесь, что программа работает — | 
			
		
	
		
			
				
					|  |  |  |  | содержимое файла из сети печатается на стандартный вывод. | 
			
		
	
		
			
				
					|  |  |  |  | Код печати `argc` и `argv` удалите. | 
			
		
	
		
			
				
					|  |  |  |  | Сделайте коммит. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Структура программы должна стать такой: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | int | 
			
		
	
		
			
				
					|  |  |  |  | main(int argc, char* argv[]) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (argc > 1) { | 
			
		
	
		
			
				
					|  |  |  |  |         // работа с cURL | 
			
		
	
		
			
				
					|  |  |  |  |         return 0; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     // прежний код построения гистограммы | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Обработка ошибок | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | В переменную `res` пример сохраняет результат `curl_easy_perform()`. | 
			
		
	
		
			
				
					|  |  |  |  | Значение `res` — числовой [код ошибки][libcurl-errors], | 
			
		
	
		
			
				
					|  |  |  |  | а если ошибок не было, `CURLE_OK` (равно 0). | 
			
		
	
		
			
				
					|  |  |  |  | Функция [`curl_easy_strerror()`][curl_easy_strerror] | 
			
		
	
		
			
				
					|  |  |  |  | принимает код ошибки и возвращает строку с текстом ошибки. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Проверяйте результат `curl_easy_perform()`, если произошли ошибка, | 
			
		
	
		
			
				
					|  |  |  |  | печатайте её текст и завершайте программу вызовом `exit(1)`. Сделайте коммит. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | [libcurl-errors]: https://curl.haxx.se/libcurl/c/libcurl-errors.html | 
			
		
	
		
			
				
					|  |  |  |  | [curl_easy_strerror]: https://curl.haxx.se/libcurl/c/curl_easy_strerror.html | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Работа с буфером, загруженным по сети | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Содержимое файла из сети нужно не печатать, | 
			
		
	
		
			
				
					|  |  |  |  | а разбирать, считывая из него те же данные, что и со стандартного ввода. | 
			
		
	
		
			
				
					|  |  |  |  | Проще всего сначала считать весь файл из сети в память. | 
			
		
	
		
			
				
					|  |  |  |  | Такое промежуточное хранилище называют *буфером*. | 
			
		
	
		
			
				
					|  |  |  |  | Затем можно организовать поток, | 
			
		
	
		
			
				
					|  |  |  |  | который будет читать не из стандартного ввода, а из буфера. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Для загрузки файла выделим функцию `download()`, | 
			
		
	
		
			
				
					|  |  |  |  | которая принимает строку с адресом файла. | 
			
		
	
		
			
				
					|  |  |  |  | Файл `<sstream>` подключает `stringstream` — | 
			
		
	
		
			
				
					|  |  |  |  | поток, который может читать из буфера в памяти. | 
			
		
	
		
			
				
					|  |  |  |  | Также в переменные типа `stringstream` можно записывать данные, | 
			
		
	
		
			
				
					|  |  |  |  | то есть заполнять буфер. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | #include <sstream> | 
			
		
	
		
			
				
					|  |  |  |  | #include <string> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ...прежние функции... | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Input | 
			
		
	
		
			
				
					|  |  |  |  | download(const string& address) { | 
			
		
	
		
			
				
					|  |  |  |  |     stringstream buffer; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // TODO: заполнить буфер. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     return input_data(buffer, false); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Обратите внимание: `CURLOPT_URL` требует адрес в виде строки C (`const char*`). | 
			
		
	
		
			
				
					|  |  |  |  | Чтобы получить её из `address` типа `string` можно как `address.c_str()`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | В зависимости от того, переданы ли программе параметры, | 
			
		
	
		
			
				
					|  |  |  |  | она получает исходные данные (`Input input`) | 
			
		
	
		
			
				
					|  |  |  |  | либо из сети, либо из стандартного ввода: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | int | 
			
		
	
		
			
				
					|  |  |  |  | main(int argc, char* argv[]) { | 
			
		
	
		
			
				
					|  |  |  |  |     Input input; | 
			
		
	
		
			
				
					|  |  |  |  |     if (argc > 1) { | 
			
		
	
		
			
				
					|  |  |  |  |         input = download(argv[1]); | 
			
		
	
		
			
				
					|  |  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |  |         input = input_data(cin, true); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     const auto bins = make_histogram(input); | 
			
		
	
		
			
				
					|  |  |  |  |     show_histogram_svg(bins); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Перенесите код работы с cURL в место, отмеченное комментарием `// TODO`. | 
			
		
	
		
			
				
					|  |  |  |  | Убедитесь, что программа компилируется | 
			
		
	
		
			
				
					|  |  |  |  | (запускать ее с параметром пока нельзя). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Сохранение данных из сети в буфер | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Как же получить доступ к данным, которые cURL загружает и (пока) печатает? | 
			
		
	
		
			
				
					|  |  |  |  | Каждый раз, когда cURL получает из сети очередной блок данных, | 
			
		
	
		
			
				
					|  |  |  |  | библиотека может вызывать функцию, которую укажет программа. | 
			
		
	
		
			
				
					|  |  |  |  | Эта функция должна добавлять полученный блок данных в буфер. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Такой элемент интерфейса библиотеки называется *callback-функцией,* | 
			
		
	
		
			
				
					|  |  |  |  | или *функцией обратного вызова.* | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Функция должна иметь такой вид: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | size_t | 
			
		
	
		
			
				
					|  |  |  |  | write_data(void* items, size_t item_size, size_t item_count, void* ctx) { | 
			
		
	
		
			
				
					|  |  |  |  |     // TODO: дописывать данные к буферу. | 
			
		
	
		
			
				
					|  |  |  |  |     return 0; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Имя функции и параметров может быть любым, | 
			
		
	
		
			
				
					|  |  |  |  | но типы данных должны быть строго такими, как описано в документации. | 
			
		
	
		
			
				
					|  |  |  |  | Ошибку может быть очень сложно отладить. | 
			
		
	
		
			
				
					|  |  |  |  | К счастью, компилятор предупреждает о несоответствии. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Параметры: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | * `void* items` — указатель на принятые данные: `item_count` блоков | 
			
		
	
		
			
				
					|  |  |  |  |     по `item_size` каждый, итого `item_size * item_count` байтов. | 
			
		
	
		
			
				
					|  |  |  |  | * `size_t item_size` — размер одного блока данных; | 
			
		
	
		
			
				
					|  |  |  |  | * `size_t item_count` — количество блоков данных; | 
			
		
	
		
			
				
					|  |  |  |  | * `void* ctx` — пользовательские данные (поясняется ниже). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Чтобы cURL продолжила загрузку, | 
			
		
	
		
			
				
					|  |  |  |  | функция должна возвращать размер принятых данных в байтах, | 
			
		
	
		
			
				
					|  |  |  |  | иначе `curl_easy_perform()` остановит работу с ошибкой. | 
			
		
	
		
			
				
					|  |  |  |  | Пока функция возвращает 0, просто чтобы возвращать какое-нибудь значение. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Указать cURL, какую функцию вызывать, можно так: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Внутри `write_data()` вычислите количество принятых байтов (см. описание | 
			
		
	
		
			
				
					|  |  |  |  | параметров), сохраните в переменную `data_size`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Далее необходимо внутри `write_data()` добавлять данные в буфер, для этого | 
			
		
	
		
			
				
					|  |  |  |  | есть специальный метод [`buffer.write()`][basic_ostream/write]: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  |     buffer.write(items, data_size); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Но это не компилируется: переменная `buffer` объявлена в `download()`, внутри | 
			
		
	
		
			
				
					|  |  |  |  | `write_data()` она недоступна. | 
			
		
	
		
			
				
					|  |  |  |  | Перенести `buffer` внутрь `write_data()` — не выход: | 
			
		
	
		
			
				
					|  |  |  |  | `buffer` нужна и в `download()`. | 
			
		
	
		
			
				
					|  |  |  |  | Объявлять в `write_data()` еще один буфер тоже неправильно: | 
			
		
	
		
			
				
					|  |  |  |  | это должен быть *тот же самый* буфер, который затем использует `download()`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Заметим, что хотя `buffer` недоступна, она существует где-то в памяти, | 
			
		
	
		
			
				
					|  |  |  |  | потому что `write_data()` вызывается из `curl_easy_perform()`, | 
			
		
	
		
			
				
					|  |  |  |  | и пока этот вызов не закончен, не закончится и `download()`. | 
			
		
	
		
			
				
					|  |  |  |  | Задача в том, чтобы получить этот адрес, | 
			
		
	
		
			
				
					|  |  |  |  | так как имея адрес переменной (то есть указатель на нее), с ней можно работать. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Это типовая необходимость и обычно библиотеки, в том числе cURL, | 
			
		
	
		
			
				
					|  |  |  |  | предоставляют в callback-функциях *пользовательские данные (user data).* | 
			
		
	
		
			
				
					|  |  |  |  | Перед тем, как позволить библиотеке вызывать функцию, | 
			
		
	
		
			
				
					|  |  |  |  | программа указывает также фиксированные данные (обычно один указатель), | 
			
		
	
		
			
				
					|  |  |  |  | которые будут передаваться библиотекой в функцию. | 
			
		
	
		
			
				
					|  |  |  |  | Именно для этого `write_data()` принимает параметр `ctx` (контекст). | 
			
		
	
		
			
				
					|  |  |  |  | Можно передавать в этом контексте адрес `buffer` так | 
			
		
	
		
			
				
					|  |  |  |  | (перед `curl_easy_perform()`): | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Контекст имеет тип `void*`, но нам известно, что это указатель на `buffer`, | 
			
		
	
		
			
				
					|  |  |  |  | то есть реальный тип — `stringstream*`. | 
			
		
	
		
			
				
					|  |  |  |  | Это как раз тот случай, когда программист может и должен | 
			
		
	
		
			
				
					|  |  |  |  | принудительно указать компилятору тип данных (приведение типов из ЛР № 1): | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | stringstream* buffer = reinterpret_cast<stringstream*>(ctx); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Поскольку `buffer` здесь указатель, | 
			
		
	
		
			
				
					|  |  |  |  | его нужно разыменовать перед вызовом метода: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | (*buffer).write(items, data_size); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Осталась последняя ошибка: если прочитать документацию на `write()`, | 
			
		
	
		
			
				
					|  |  |  |  | она принимает первым параметром `const char*`, а `items` имеет тип `void*`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Сделайте приведение типа для `items`, чтобы программа компилировалась. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Конструкция `(*buffer).write(...)` выглядит громоздко, потому в C++ введен | 
			
		
	
		
			
				
					|  |  |  |  | специальный оператор стрелки, который позволяет вызвать метод по указателю | 
			
		
	
		
			
				
					|  |  |  |  | красивее (параметры `write()` прежние, они опущены для краткости): | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ```cpp | 
			
		
	
		
			
				
					|  |  |  |  | buffer->write(...); | 
			
		
	
		
			
				
					|  |  |  |  | ``` | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Самостоятельно.** | 
			
		
	
		
			
				
					|  |  |  |  | Добейтесь компиляции и проверьте корректную работу программы с примером | 
			
		
	
		
			
				
					|  |  |  |  | из задания. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | [basic_ostream/write]: https://en.cppreference.com/w/cpp/io/basic_ostream/write | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ### Индивидуальные задания | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Всю необходимую информацию по функциям cURL нужно изучить по официальной | 
			
		
	
		
			
				
					|  |  |  |  | документации и на защите предъявить ссылку на конкретную страницу. | 
			
		
	
		
			
				
					|  |  |  |  | В частности, если не указан конкретный параметр `curl_easy_setopt()`, | 
			
		
	
		
			
				
					|  |  |  |  | нужно подобрать его по описанию в списке. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Во всех вариантах нужно обрабатывать ошибки cURL, | 
			
		
	
		
			
				
					|  |  |  |  | как это делается в основной программе. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Если вариант требует ввода дополнительных данных, проверить работу с файлом | 
			
		
	
		
			
				
					|  |  |  |  | «из сети» можно через локальный файл и адрес вида | 
			
		
	
		
			
				
					|  |  |  |  | `file://C:/Users/kozlyuk/Desktop/marks.txt` для файла `marks.txt` на рабочем | 
			
		
	
		
			
				
					|  |  |  |  | столе пользователя `kozlyuk`. Полный путь к файлу можно получить, щелкнув | 
			
		
	
		
			
				
					|  |  |  |  | в адресной строке «Проводника» (так же, как перед запуском `cmd`). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 1 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: пользователь задает ширину гистограммы --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | суммарное время (total), затраченное на загрузку файла по сети. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 2 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: автоматический расчет цвета столбцов --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-format`, которая определяет формат вывода: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -format text` — текст, | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -format svg` — изображение. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -format svg http://...` или `lab04.exe http://... -format svg`. | 
			
		
	
		
			
				
					|  |  |  |  | Если после `-format` не стоит `text` или `svg`, нужно печатать сообщение | 
			
		
	
		
			
				
					|  |  |  |  | с подсказкой, как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 3 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: пользователь задает толщину столбца --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | скорость загрузки файла по сети. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 4 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: высоты столбцов в процентах --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-bins`, которая позволяет задать количество | 
			
		
	
		
			
				
					|  |  |  |  | корзин при запуске. Считывать его в `read_input()` при этом не нужно. | 
			
		
	
		
			
				
					|  |  |  |  | Например: `lab34.exe -bins 3`. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -bins 3 http://...` или `lab04.exe http://... -bins 3`. | 
			
		
	
		
			
				
					|  |  |  |  | Если после `-bins` не стоит числа, нужно печатать сообщение с подсказкой, | 
			
		
	
		
			
				
					|  |  |  |  | как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 5 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 1/3: зеркальное отображение --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-generate`, при указании которой программа будет | 
			
		
	
		
			
				
					|  |  |  |  | генерировать числа вместо их считывания. Количество столбцов считывать | 
			
		
	
		
			
				
					|  |  |  |  | по-прежнему нужно. Например, `lab34.exe -generate 100` генерирует 100 чисел. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -generate 10 http://...` или `lab04.exe http://... -generate 10`. | 
			
		
	
		
			
				
					|  |  |  |  | Если после `-generate` не стоит числа, нужно печатать сообщение с подсказкой, | 
			
		
	
		
			
				
					|  |  |  |  | как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 6 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: пользователь задает цвета столбцов --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | размер файла, загружаемого по сети. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 7 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: цвет столбца зависит от высоты соседних --> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | При помощи `CURLOPT_PROGRESSFUNCTION` добавьте в программу отображение | 
			
		
	
		
			
				
					|  |  |  |  | прогресса загрузки файла. Каждый раз, когда cURL рапортует прогресс, | 
			
		
	
		
			
				
					|  |  |  |  | печатайте строку вида: `Progress: 42%`. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | **Указание.** | 
			
		
	
		
			
				
					|  |  |  |  | Чтобы cURL рапортовала прогресс, нужно установить `CURLOPT_NOPROGRESS` в 0. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 8 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: ввод размера шрифта с проверками --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | IP-адрес сервера, с которого скачан файл (не local). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 9 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: ввод размера "блока" гистограммы --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_version_info()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | версию cURL и версию OpenSSL в строковом виде. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 10 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 1/3: вертикальная гистограмма, подписи сверху --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-verbose`, при указании которой нужно включать | 
			
		
	
		
			
				
					|  |  |  |  | отладочный вывод cURL с помощью `CURLOPT_VERBOSE`. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -verbose http://...` или `lab04.exe http://... -verbose`. | 
			
		
	
		
			
				
					|  |  |  |  | Если встретился иной аргумент, начинающийся с дефиса, нужно печатать сообщение | 
			
		
	
		
			
				
					|  |  |  |  | с подсказкой, как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 11 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: рамка вокруг гистограммы --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_version_info()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | список протоколов, которые поддерживает cURL (`protocols`). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 12 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 1/3: отметки на оси --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-fill` для задания цвета заливки столбцов. Например, | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -fill red` или `lab04.exe -fill "#ff0000"` заливает их красным. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -fill red http://...` или `lab04.exe http://... -fill red`. | 
			
		
	
		
			
				
					|  |  |  |  | Если после `-fill` не стоит еще одного аргумента, нужно печатать сообщение | 
			
		
	
		
			
				
					|  |  |  |  | с подсказкой, как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 13 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 1/3: вертикальная гистограмма, подписи снизу --> | 
			
		
	
		
			
				
					|  |  |  |  | Добавьте программе опцию `-stroke` для задания цвета границ столбцов. | 
			
		
	
		
			
				
					|  |  |  |  | Например, `lab34.exe -stroke red` или `lab04.exe -stroke "#ff0000"` делает их | 
			
		
	
		
			
				
					|  |  |  |  | красными. | 
			
		
	
		
			
				
					|  |  |  |  | Опция может стоять до или после URL: | 
			
		
	
		
			
				
					|  |  |  |  | `lab34.exe -stroke red http://...` или `lab04.exe http://... -fill red`. | 
			
		
	
		
			
				
					|  |  |  |  | Если после `-stroke` не стоит еще одного аргумента, нужно печатать сообщение | 
			
		
	
		
			
				
					|  |  |  |  | с подсказкой, как запускать программу, и завершать работу. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 14 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: пунктирные линии с вводом их параметров --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | размер запроса, который cURL отправила серверу (request size). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 15 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: горизонтальная шкала с расчетами --> | 
			
		
	
		
			
				
					|  |  |  |  | Установите callback-функцию `CURLOPT_HEADERDATA`, откуда печатайте | 
			
		
	
		
			
				
					|  |  |  |  | полученные данные на стандартный вывод ошибок. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 16 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: ввод цвета линий для каждого столбца --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | время, затраченное на установку соединения с сервером (connect). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 17 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: автоматический расчет прозрачности --> | 
			
		
	
		
			
				
					|  |  |  |  | Устанавливайте опцию `CURLOPT_FAILONERROR` и анализируйте результат | 
			
		
	
		
			
				
					|  |  |  |  | `curl_easy_perform()`, чтобы проверить результат загрузки файла, | 
			
		
	
		
			
				
					|  |  |  |  | а не выполнения запроса. Придумайте способ тестирования. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #### Вариант 18 {.unnumbered} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | <!-- ЛР 3: ввод стиля надписей --> | 
			
		
	
		
			
				
					|  |  |  |  | С помощью функции `curl_easy_getinfo()` печатайте на стандартный вывод ошибок | 
			
		
	
		
			
				
					|  |  |  |  | время, затраченное на получение IP-адреса сервера по его имени (name lookup). | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Контрольные вопросы | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 1. Сколько аргументов в команде `git add -p file.txt`? Чему равно `argc`? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Из каких файлов и как собирается программа, использующая библиотеку? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Чем отличаются статические и динамические библиотеки? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Как расшифровывается термин «API» и что представляет собой API библиотеки? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Что такое дескриптор (handle), для чего он предназначен? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Что такое функция обратного вызова (callback)? | 
			
		
	
		
			
				
					|  |  |  |  | 1. Зачем и как применяются пользовательские данные в callback-функциях? |