Добавлен , опубликован

Основы программирования Корсаров

Содержание:
На прошлом уроке мы выяснили, что при помощи директивы #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().
if(LoadSegment("store\initGoods.c"))
{
    InitGoods();
    UnloadSegment("store\initGoods.c");
}
Есть еще функции, которые доступны в скриптах, но находятся внутри движка (в .dll). Они также предварительно объявляются, но с ключевым словом native:
native float Clampf(float fValue);

`
ОЖИДАНИЕ РЕКЛАМЫ...
38
ПРАВИЛО Код должен находится там где ему полагается - в файлах .с и только там
Это только для корсаров правило?
Потому что в крестах иногда в небольших проектах смело пишут весь код в .h файлах и вообще не юзают .cpp
Подробнее про минусы такого подхода stackoverflow.com/questions/193864/pros-cons-of-putting-all-code...
Ответы (3)
23
ScorpioT1000, да нет, правило вполне себе общее.
Я так и не понял, что тот чувак по ссылке пытается выиграть, перерося код в хедеры. Время компиляции? Это чушь и ему там это сто раз обяснили.
Добиться схожести с джавой? Весь код в одном файле?
Это как переносить другую игру на варкрафт, или переносить варкрафт на движок другой игры.
Через Ж всегда можно сделать. А я пытаюсь учить людей делать правильно))
38
avuremybe, я тоже неплохо себе писал код в h файлах, жопа не отвалилась)
23
ScorpioT1000, ты же понимаешь, что "работает хорошо" и "работает правильно" это разные вещи.
Не стоит в цикле основ учить людей хитровылюбленным технологиям))
О возможных способах налюбить систему и их последствиях, как правило, пишут в специализированных темах для тех, кто уже разбирается.
30
что никакого компилятора нет
Вообще никакого? Код прям из текстовых файлов исполняется без компиляции?
Ответы (3)
23
nazarpunk, чисто технически, имя класса этой читалки COMPILER.
Но как по мне - это толи парсер, толи интерпретатор (я не силен в таких штуках). Оно на ходу их читает, да. Можно прям в запущенной игре код менять и файлы кусками подключать/отключать.
Код не исполняется напрямую. Оно его читает, ищет знакомые слова и вызывает нужные функции. Это интерпретацией же называют?
30
Это интерпретацией же называют?
Разница тонкая. Если он прям из букв исполняет, то интерпретация. Если предваритеьно превращает во внутреннее предстваление, то компиляция.
23
nazarpunk, из того, что я разобрал: каждой функции присвоен так называемый токен. Он видит ключевое слово, получает ее токен, далее по нему решается что с этим делать - если это, предположим, инклуд, то надо подключить еще файл, если это ф-ция - то исполнить функцию.
Это надо садиться и целенаправленно разбирать этот их компайлер. Я более приоритетными вопросами озадачен, поэтому сильно не вникал пока что. Но в перспективе хочу разобраться, конечно. Штука интересная))
Какой-то предварительный анализ при запуске тоже есть, но до него я в коде не докопался. Можно прошляпить что-то по синтаксису (точку с запятой, например), а оно нормально запускается и еще и исполняет код.
А бывает напишешь обращения к функциям неправильно (лишние аргументы, например), а оно все равно работает. Но в точке исполнения этого обращения обрывает блок кода, из которого это вызвано, пишет в логи, но работает дальше.
А иногда можно заюзать какие-то конструкции, которые он не поддерживает. И тогда он ошибку при запуске выдает. Отсюда вывод, что какой-то анализ он таки делает при запуске, вот только я не знаю какой.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.