На прошлом уроке мы выяснили, что при помощи директивы #include можно подключить внешний файл с кодом. Внимательный читатель мог заметить, что некоторые из подключаемых файлов имеют расширение .h вместо .c. Об их роли мы сегодня и поговорим.
Заголовочные файлы
Header file (заголовочный файл) - хранит набор объявлений объектов для их последующего использования в других программах (или в разных частях одной программы). Имеет расширение .h
В чистом С++ нельзя использовать функцию, пока она не объявлена. Иначе программа не скомпилируется. Но часто бывает так, что вам нужно использовать функцию, которая объявлена где-то в другом месте. Или вы добавили новую функцию и теперь нужно перебирать всю программу, чтобы данная функция располагалась ниже тех, которые она использует. А это не только сложно и трудозатратно, но и не всегда возможно в принципе. Бывают вообще ситуации, когда две функции вызывают друг друга. Какую из них вы бы не поставили выше другой - ничего не получится.
Это решалось предварительным объявлением:
int calc(int x, int y);
Такая запись говорит компилятору, что где-то есть функция с именем calc() и она принимает два аргумента - x и y. Это позволяет использовать данную функцию даже когда она еще не определена.
Справка:Предварительное объявление функции состоит из типа возвращаемых данных, имени функции и заключенных в скобки принимаемых аргументов. Тела функции нет, а в конце строки обязательно стоит точка с запятой.
Наборы таких предварительных объявлений собирались в заголовочные файлы, которые в свою очередь, подключались к файлам, где программист планировал использовать данные функции.
Вы уже, наверное, догадались, что я не просто так говорю это в контексте чистого С++.
ОСОБЕННОСТЬ скриптовой части Корсаров заключается в том, что никакого компилятора нет. Все файлы скриптов парсятся специальным модулем в движке игры.
Справка:Парсинг («parsing») – это принятое в информатике определение синтаксического анализа. Для этого создается математическая модель сравнения лексем с формальной грамматикой, описанная одним из языков программирования.
Таким образом, у нас нет ни потребности соблюдать порядок объявлений функций, ни делать их предварительные объявления.
Но файлы заголовков всё равно используются, справедливо заметите вы. Да, ведь именно с этого я начал данный урок.
Но файлы заголовков всё равно используются, справедливо заметите вы. Да, ведь именно с этого я начал данный урок.
Что происходит на самом деле
Изначально разработчиками Корсаров заголовочные файлы использовались для двух вещей: вынесение туда всех дефайнов и массивов строк диалогов, для облегчения их последующего перевода на другие языки.
Бегло пробежавшись по файлам скриптов, я обнаружил в заголовочных файлах несколько объявлений глобальных переменных, а также три файла, в которых есть определения полноценных функций. Добавлены они, судя по всему, одним из модмейкеров. Это пример плохого кода и следовать ему не нужно.
ПРАВИЛО Код должен находится там где ему полагается - в файлах .с и только там.
Используйте файлы заголовков по назначению - в них должны находиться только константные переменные, роль которых выполняет директива #define.
И все таки...
И всё таки в некоторых местах вы наверняка встречали строки, подобные этим:
extern void wdmInitWorldMap();
extern void InitGoods();
extern void InitStores();
Это кусок кода из файла seadogs.c, с которого начинается выполнение скриптов игровым движком.
Цитата от AlexusB:
Есть большой набор файлов, которые не грузятся в ОЗУ сразу, не занимают ресурс игры, а подгружаются на время, потом выгружаются.
Это:
- все диалоги
- все интерфейсы
- большинство инитов начала игры
- DEBUG-панели и DEBUG-читы, что делал я
Если вызвать функцию, которой нет на момент запуска игры - получите ошибку. Для избежания этого используется предварительное объявление с ключевым словом extern.
Сама ситуация запуска игры с отсутствующей частью кода возможна только в случае, когда этот код подгружается на время где-то в процессе работы при помощи функции LoadSegment(), а потом снова выгружается с помощью UnloadSegment().
Сама ситуация запуска игры с отсутствующей частью кода возможна только в случае, когда этот код подгружается на время где-то в процессе работы при помощи функции LoadSegment(), а потом снова выгружается с помощью UnloadSegment().
if(LoadSegment("store\initGoods.c"))
{
InitGoods();
UnloadSegment("store\initGoods.c");
}
Есть еще функции, которые доступны в скриптах, но находятся внутри движка (в .dll). Они также предварительно объявляются, но с ключевым словом native:
native float Clampf(float fValue);
Ред. ScorpioT1000
Потому что в крестах иногда в небольших проектах смело пишут весь код в .h файлах и вообще не юзают .cpp
Это как переносить другую игру на варкрафт, или переносить варкрафт на движок другой игры.
Ред. avuremybe
Не стоит в цикле основ учить людей хитровылюбленным технологиям))
О возможных способах налюбить систему и их последствиях, как правило, пишут в специализированных темах для тех, кто уже разбирается.
Ред. avuremybe
Ред. avuremybe
А бывает напишешь обращения к функциям неправильно (лишние аргументы, например), а оно все равно работает. Но в точке исполнения этого обращения обрывает блок кода, из которого это вызвано, пишет в логи, но работает дальше.
А иногда можно заюзать какие-то конструкции, которые он не поддерживает. И тогда он ошибку при запуске выдает. Отсюда вывод, что какой-то анализ он таки делает при запуске, вот только я не знаю какой.