Как-то вот так получается:
Место и назначение extren и «склеивающих» заголовочников в многофайловом проекте.
Стандартный многофайловик включает в себя главный комплект файлов
ino_main.h
ino_main.cpp (ino_main.c)
и несколько дополняющих комплектов файлов...
К примеру:
библиотека 1 – общая, но входит в состав компилятора «по умолчанию»
std_lib_ext.h
std_lib_ext.cpp
библиотека2 — полная самоделка
my_lib_ext.h
my_lib_ext.cpp
обнаружившийся обобщенный фрагмент, который пришлось вынести(«выкусь»)
my_kus.h
my_cus.cpp
Помимо прочего, относительно этих файлов имеются стандартные заголовочники комплектных библиотек компилятора.
Однако в случае с ардуино дело несколько похитрее — там набор «по умолчанию» в заголовочнике главного комплекта НЕ УКАЗЫВАЕТСЯ (уже где-то авторами IDE прописан и спрятан). А вот для дополнительных комплектов файлов выглядит как
//------------------------------------------------------------------------
// обязательная "магическая добавка" (начало)
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
// обязательная "магическая добавка" (конец)
//------------------------------------------------------------------------
или в более современном варианте
#include "Arduino.h"
а уже в этом ( Arduino.h) заголовочнике проставлено все, что входит в состав референса «по умолчанию».
Как пример минимального обобщения — подключение заголовочников дополнительных комплектов файлов в заголовочнике главного комплекта
файл ino_main.h в нашем примере относительно ардуино должен содержать:
#include “my_lib_ext.h»
#include “my_kus.h»
файл подключения библиотек по умолчанию там уже есть (в отличии от «чистого Си, где их надо самому прописывать).
Для того, чтобы нормально работали самодельные внешние комплекты в каждом из них надо обязательно добавить «магическую добавку».
В принципе... Ежли бы дело касалось простых самодельных библиотек тем бы и ограничилось. Так как заголовочники внешних комплектов входят в состав заголовочника ino_main.h то все, что объявлено в их заголовочниках будет общим для содержимого в ino_main.cpp (ino_main.c).
Однако уже тут, на ранней стадии распределения объявлений могут получиться накладки.
Если имеется пара внешних комплектов файлов, имеющих общую часть.
Допустим та же кодовая таблица символов семисегментного индикатора.
Она общая и для драйвера дисплея и для главного комплекта и для «третьей стороны»...
В таком случае появляется внешний «объединяющий заголовочник» (можно и «мостом» назвать — кому как приятнее).
В том заголовочнике прописываются общие объявления констант и типов, применимо к соответствующим файлам комполектов. В моих последних проектах-примерах это dhsct.h
Соответственно он будет подключаться во всех заголовочниках
файл ino_main.h в нашем примере относительно ардуино должен содержать:
#include “my_lib_ext.h»
#include “my_kus.h»
#include “dhsct.h”
файл my_lib_ext.h будет содержать
#include "Arduino.h"
#include “dhsct.h”
а файл my_kus.h
#include "Arduino.h"
#include “dhsct.h”
При простом общем содержимом в виде дефайнов констант, типов этого достаточно.
Хуже, если появляется необходимость использования переменных, функций, объектов которые объявляются и инициализируются в одном комплекте файлов, а использоваться будут в разных комплектах за пределами «родительского» комплекта.
Пока использование внешне объявленных переменных касается только пределов главного комплекта файлов особо вопросов не возникает — там заголовочник библиотеки подключен в заголовочник главного комплекта «по умолчанию».
Но вот возникает необходимость выноса части текста исходника за его пределы...
И понеслось...
my_lib_ext.h УЖЕ является частью ino_main.h
Даже ежли мы его подключим в my_kus.h это решит лишь вопрос по константам да некоторым типам....
А ведь в самой «выкуси» наверняка полно перекрестных ссылок, в том числе и на компоненты «внешних комплектов». Причем в состав тех компонентов входят и данные и объекты и функции...
Вот тут и всплывает extern...
А описания по ее применению таки совсем маловато... Относительно простых для восприятия...
По своей специфике extern таки лучше объявлять в отдельном *.h файлике, входящем в состав тех заголовочников, в которых имеется описание или применение того, что мы как extern объявили.
Причем описание (и инициализация) должна быть только в одном из комплектов файлов, а применение разрешено во всех, где подключен заголовочник с объявлением.
Как пример:
функция func(); описана в комплекте my_lib_ext, а применение ее будет происходить как в комплекте ino_main, так и в комплекте my_kus
создаем most.h в котором указываем
extern void func();
в файлах, где рассматривается применение func(); и ее описание(определение) в заголовочниках добавляем
#include “ most.h”
Хотя... как вариант:
объявление и описание находятся в комплекте файлов my_lib_ext
а уже заголовочник my_lib_ext.h подключаясь и в ino_main.h и в my_kus.h автоматически делает func(); доступной в пределах этих комплектов файлов.
(Так сделана библиотечка dscrc в inc_test2 варианте).
И в дополнение:
Не забываем в каждом из собственнолапных *.h файлов ставить защиту от переопределения:
// защита от переоперделения с заключительным
// #endif в конце файла
#ifndef NAME_H // на кириллице это ТЬФУ — имя нашего *.h файлика
#define NAME_H
далее содержимое
и завершается
#endif
Надо б на каком-нить проектике проверить...
