Jump to content
Sign in to follow this  
FortRoss

Вопросы по созданию приборов на С++

Recommended Posts

gosha-z

Ну значит я ошибся.

Хотя, MS точно не рекомендовала использовать его. Этот текст на англицком я точно видел. Сейчас некогда искать.

Не рекомендовал. Но для НОВЫХ разработок. Коей сим не является :)

Share this post


Link to post
Share on other sites
seyco

Всем привет. Почитал SDK собрал прибор с двумя выключателями, все работает. Почитал эту тему и решил сделать приборы без таблицы GAUGE_TABLE_BEGIN().

Тыркался, тыркался, вроде получилось (один прибор воткнуть), а как только добавил выключатели, проект перестал собираться. Посмотрите пожалуйста кто может, где я что не так сделал. В проекте только 2 выключателя.

 

Еще немного потыркался, проблема получается в объявлении переменных. Если я объявляю их в той же срр, проблем нет, собираются эти выключатели и работают в симе. Но как только выношу в отдельный ...h, или в source.h, появляется полотенце ошибок. Подскажите плиз, где копать?

Edited by seyco

Share this post


Link to post
Share on other sites
serg_p

Переменные не могут определяться в h файле. Тут нужно понять разницу межу декларированием (объявление) и определением. Декларирование лишь только оповещает компилятор, что есть такой-то тип данных, такая-то константа, такая-то функция, и т.д. и в случае, если это декларирование будет использовано в разных отдельно компилируемых модулях - оно выносится в отдельный h файл, который потом будет инклудиться в модулях, где эти объявления нужны. Определение же всегда заставляет компилятор отвести под объект память, а если это функция, то и скомпилировать ее в машинный код. Собственно с объектами, которые именно определены, а непросто продекларированы - уже можно работать. Функции, которые определены - можно вызывать, в отличие от случая, когда они лишь объявлены. В последнем случае Вас непременно будет ждать ошибка на этапе компановки.

 

Переменные могут быть объявлены через extern или определены. Определяются переменные только в c/cpp, т.е. непосредственно в отдельно компилируемом модуле, как статические переменные (т.е. переменные, живущие все время работы программы) или в функции, как автоматические (живущие только пока выполняется функция), ну и так же при использовании ключевого слова static внутри функции, как статические переменные,.

 

Как получить значение переменной в другом компилируемом модуле.

1. В модуле, где необходимо видеть переменную, определенную в другом модуле делается объявление:

extern "тип переменной" "имя переменной" {[количество элементов массива]};

Инициализации здесь быть не может.

Но это не очень хороший путь.

2. Через функции:

Делаются отдельные функции для чтения и записи. Определяются естественно в cpp, где находятся нужные переменные и объявляются в h файле, который потом включается по инклуду в те модуле, где эти функции будут вызываться. Это не только изолирует от непосредственного доступа к переменным, ведь не всегда нужно писать, но и в случае записи, можно в функцию записи подсадить, какой-нибудь код, который в процессе записи будет делать, что-нибудь полезное. Тут получается, неявным образом мы подсаживаем обработчик события записи в данную переменную.

Если функция должна только вернуть значение переменной, то для повышения быстродействия можно определить в h файле inline функцию (это единственный случай, когда функция определяется в h файле, далее она будет не вызываться, а включаться, т.е. все ее тело будет скопировано в место вызова) прибегнуть к следующей хитрости.

Пример:

public.cpp

...

char pathToSim[MAX_PATH]; // определение переменной в отдельно компилируемом модуле public

 

public.h

...

__inline char* GetPathToSim() { extern char pathToSim[MAX_PATH]; return pathToSim; }

 

Поскольку в h файле pathToSim еще не известна (она определятся в cpp) - внутри inline функции мы ее объявляем через extern

 

Это все C. В случае ООП, т.е. в случае C++ все эти вещи решаются более естественно, можно сказать - более изящно.

 

 

seyco

 

Вам нужно убрать из source.h

FLOAT64 m_battery = 0; //выключатель гл. аккумулятора

FLOAT64 panel_lamp = 0; //выключатель освещения приборов

определить их в соответствующих компилируемых модулях, а в тех, других модулях, где дополнительно нужен доступ к нип - сделать так, как описано в одном из моих вариантов.

Edited by serg_p

Share this post


Link to post
Share on other sites
icebear

Переменные не могут определяться в h файле.

....

 

Серёга, не соглашусь по двум пунктам:

1 - компилятор не различает h и cpp файлы, ибо в процессе компилирования это сливается всё в один большой файл (грубо говоря), поэтому почему нельзя декларировать переменные в h мне непонятно. Естественно можно получить по ушам на этапе компиляции таких файлов, если заголовок инклудируется многоразово без защиты от вложеного инклуда (тогда будет просто попытка создать и инициализировать переменные n-ое кол-во раз, где n - кол-во инклудов заголовка)

2 - декларация extern вполне себе нормальный подход, это обычные глобальные переменные.

Share this post


Link to post
Share on other sites
serg_p
... 1 - компилятор не различает h и cpp файлы, ибо в процессе компилирования это сливается всё в один большой файл (грубо говоря), поэтому почему нельзя декларировать переменные в h мне непонятно. ...

Что значит декларировать?

Декларировать переменные можно только в случае использования extern. Случай импорта и экспорта из dll не рассматриваем.

Если ты имел в виду случай, когда переменные определяются в h файле, то да, это можно, если h файл включается по инклуду только один раз. Но зачем использовать вещи не по назначению. h файл - это файл для объявлений и неправильное его использование может встать в будущем поперек.

 

... 2 - декларация extern вполне себе нормальный подход, это обычные глобальные переменные.

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

 

Видел бы ты то наследие, с которым мне приходится работать. Достаточно в одном, отдаленном месте пернуть, как в другом словишь геморой. Там все переменные глобальные. Все тычачи. Но тут, либо все писать снова самому, либо использовать то, что есть и потихоньку, там где можно переделывать. Как это я сделал в Ил-86 и в ПТ Ил-62М для FSX, над которым я уже не работаю.

Edited by serg_p

Share this post


Link to post
Share on other sites
icebear

Что значит декларировать?

Декларировать переменные можно только в случае использования extern. Случай импорта и экспорта из dll не рассматриваем.

Если ты имел в виду случай, когда переменные определяются в h файле, то да, это можно, если h файл включается по инклуду только один раз. Но зачем использовать вещи не по назначению. h файл - это файл для объявлений и неправильное его использование может встать в будущем поперек.

 

Да, имелось декларирование с инициализацией в h, который включается по инклуду один раз. h - это заголовок, сиречь файл для деклараций констант, переменных и т.п. Что говорит против декларации с инициализацией (ибо многие С компиляторы не инициализируют переменные по умолчанию)?

 

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

 

Всё верно, подписываюсь, с оговоркой, что справедливо для С++. Сим же в первую очередь обычный процедурный С. У нас в тамагочи допустим есть класс CGraphicResourceManager, который оборачивает общие сервисы, расчитаные на работу с GDI+. Этот класс создаётся один раз при инициализации модуля (у нас по факту одна гауга) и extern'ится куда надо, ибо иногда нужно дёрнуть метод этого класса из колбэка гауги, а не из нормального класса, описывающего конкретную систему.

Share this post


Link to post
Share on other sites
serg_p
... Да, имелось декларирование с инициализацией в h, который включается по инклуду один раз. ...

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

Edited by serg_p

Share this post


Link to post
Share on other sites
seyco

Спасибо, все в принципе ясно, только вот одно покоя не дает, первый мой проект с выключателями работает нормально (по SDK-шному примеру), причем переменные я объявлял в заголовочном файле обычным способом, я здесь убрал еще один большой модуль радиовысотомера, для простоты...

 

Здесь я уже понял что к чему...

Edited by seyco

Share this post


Link to post
Share on other sites
serg_p

Некогда смотреть.

 

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

Share this post


Link to post
Share on other sites
serg_p

icebear

 

Андрей, ша напишу по поводу зла у тебя.

 

... Всё верно, подписываюсь, с оговоркой, что справедливо для С++. Сим же в первую очередь обычный процедурный С. У нас в тамагочи допустим есть класс CGraphicResourceManager, который оборачивает общие сервисы, расчитаные на работу с GDI+. Этот класс создаётся один раз при инициализации модуля (у нас по факту одна гауга) и extern'ится куда надо, ибо иногда нужно дёрнуть метод этого класса из колбэка гауги, а не из нормального класса, описывающего конкретную систему.

 

Вот это зло.

 

Используя указатель через extern в других модулях, ты можешь, в каком-нибудь методе/функции его лего убить.

А если сделаешь так в общевключаемом h файле:

__inline CObj* GetPtrObj() { extern CObj* ptrObj; return ptrObj; }

И далее везде будешь использовать GetPtrObj(), то хрен кто когда испортит твой указатель.

Дел на копейку, а выгоды на рубль.

 

Что бы не закралось сомнений по поводу того, во что в результате компиляции превращается { extern CObj* ptrObj; return ptrObj; } - можно включить генерацию ассемблерных листингов и посмотреть.

 

Вот пример.

Исходный код:

__inline char* GetPathToSim() { extern char pathToSim[MAX_PATH]; return pathToSim; }

...

sprintf_s(path_to_sound, MAX_PATH, "%s%s", GetPathToCFG(), "SOUND\\");

 

А вот, что в asm файле:

; 95 : sprintf_s(path_to_sound, MAX_PATH, "%s%s", GetPathToCFG(), "SOUND\\");
push OFFSET [email protected][email protected][email protected]
push OFFSET [email protected]@3PADA ; pathToCFG
push OFFSET [email protected][email protected][email protected]
push 260	 ; 00000104H
lea ecx, DWORD PTR _path_to_sound$[ebp]
push ecx
call _sprintf_s
add esp, 20	 ; 00000014H

 

Видно, что непосредственно используется переменная pathToCFG, хотя в C был вызов функции GetPathToCFG(). Вот в этом вся и фишка. В C коде это inline функция, которой естественно ничего присвоить нельзя (нельзя испортить значение переменной), а в машинном коде - это просто переменная.

Edited by serg_p

Share this post


Link to post
Share on other sites
icebear

Спасибо, все в принципе ясно, только вот одно покоя не дает, первый мой проект с выключателями работает нормально (по SDK-шному примеру), причем переменные я объявлял в заголовочном файле обычным способом, я здесь убрал еще один большой модуль радиовысотомера, для простоты...

 

Можно узнать Ваши познания в С++? Потому что никто здесь С/С++ преподавать не будет, это я Вам точно говорю.

Share this post


Link to post
Share on other sites
seyco

Познания мои, ну как Вам сказать, читал много, практики очень мало. Работал пару лет в ассемблере (программирование микроконтроллеров), там конечно попроще. Я не прошу меня обучать С++, теорию я знаю хорошо, спасибо огромное Сергею П., очень все хорошо и понятно объясняет, пытаюсь вникнуть во все тонкости, чтобы потом не терять время, наступая на грабли... Если б я не читал форум, я б сделал как в СДК написано (по простому, без наворотов). Интересно сами разработчики сима по СДК-шной схеме самолеты собирали?

Обидно конечно, что этот форум создан только для профессионалов...

Share this post


Link to post
Share on other sites
serg_p
... Если б я не читал форум, я б сделал как в СДК написано (по простому, без наворотов). Интересно сами разработчики сима по СДК-шной схеме самолеты собирали? ...

Вот это очень важный момент. Он не понимается начинающими программистами. Не понимается потому, что еще нет опыта, а пример в SDK - просто фуфло. А далее, если понимание не приходит - получается один отдельно компилируемый модуль с сотнями приборов. Это случай из ряда вон. Я не знаю строила ли по этой схеме MS, что-нибудь, но пример - ужасный. Хотя, у MS все дефолтные самолеты особо-то, какой-то сложностью не отличаются. Мож и действительно налепили так.

 

Здесь сразу стоит понять и практиковать - один прибор - один отдельно компилируемый модуль.

 

Определения макросов:

#define GAUGE_NAME "Наименование прибора"

#define GAUGEHDR_VAR_NAME gaugehdr_{имя хедера прибора}

#define GAUGE_W {ширина прибора}

запихиваем непосредственно в отдельно компилируемый модуль с прибором, а gaugehdr_{имя хедера прибора} объявляем в главном модуле, через extern:

extern GAUGEHDR gaugehdr_{имя хедера прибора}

 

Здесь от extern никуда не деться, ибо это:

void FSAPI   module_init(void)
{
 // Здесь вызываем весь код инициализации
}

void FSAPI   module_deinit(void) 
{
   // Здесь вызываем весь код деинициализации
}

BOOL WINAPI   DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)    
{                                                        
   return TRUE;                                        
}

GAUGESIMPORT   ImportTable =                            
{                                                        
   { 0x0000000F, (PPANELS)NULL },
   { 0x00000000, NULL }
};              

extern GAUGEHDR   gaugehdr_{имя хедера прибора}
... // Объявление хедеров всех остальных приборов

GAUGESLINKAGE   Linkage =							
{													
 0x00000013,										
 module_init,									
 module_deinit,									
 0,												
 0,
 FS9LINK_VERSION,
 {
         (&gaugehdr_{имя хедера прибора}),
         ... // инициализация указателей на хедеры всех остальных приборов,
	  0
 }
};

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

 

Дополнительно, кроме структуры Linkage, я привел пример раскрытия макроса GAUGE_TABLE_BEGIN(). Без этого не возможно использовать очень полезные функции module_init и module_deinit. Собсвенно все функции инициализации и соответсвенно деинициализации нужно вызывать из них. Они срабатывают один раз при загрузке gau (dll) и при ее выгрузке.

Edited by serg_p

Share this post


Link to post
Share on other sites
seyco

Я согласен с Вами Сергей, что всё лепить в один модуль не хорошо, хоть и работает. Поэтому и остановился на изучении других вариантов, но здесь без опыта и без другого описания (кроме как СДК) у начинающего прибориста, после прочтения кучи информации (нужной и не нужной), мозги не только кипеть начинают... А бросать, начатое полгода назад, совсем не хочется.

Share this post


Link to post
Share on other sites
icebear

Господа, а что вы понимаете под одним модулем? Одна гауга на кучу приборов или один файл на кучу гауг?

 

seyco, познания интересны, потому что многие вещи в написании гауг можно понять самому имея опыт в С. например тот же самый linkage. как он построен можно увидев декларацию макроса. но если вы начнёт задавать вопросы типа "что такое макрос" - то вам здесь помощи не будет.

Share this post


Link to post
Share on other sites
serg_p

Адрюх, ты, похоже - весь в Java.

 

Отдельно компилируемый модуль - это один cpp с реализаций и один h с интерфейсом (в результате компиляции, не сборки, а именно копиляции, получается отдельный obj и он, если cpp не трогали и будет просто браться компановщиком в следующий раз - вот раздельная компиляция модулей). Если чего поменял в cpp то компилируется только он, другие cpp не трогаются (если конечно они не включены по инклуду, в какой-нибудь cpp, как в идиотском примере из SDK). В каждом таком cpp, т.е. в отдельно компилируемом модуле и размещаем по одному прибору. Т.е. по одному GAUGE_HEADER_FS700( ... со всеми визуальными элементами этого прибора.

 

Результат - раздельная компиляция модулей, т.е. раздельная компиляция кода приборов. + Инкапсуляция внутрь отдельно компилируемого модуля всех статических переменных, которых создается целая куча макросами создания визуальных элементов + инкапсуляция своих статических переменных, если будут.

 

P.S.

Естественно cpp должен быть включен в проект и на нем не должна стоять опция Excluded From Build.

Edited by serg_p

Share this post


Link to post
Share on other sites
seyco

Здесь сразу стоит понять и практиковать - один прибор - один отдельно компилируемый модуль.

 

 

Тут я давно уже решил для себя сделать не только прибор в отдельно компилированном модуле, но и выключатели. И проще искать их будет для редактирования и перекомпилировать быстрее. Может я и не совсем прав...

 

Здесь от extern никуда не деться, ибо это:...

 

 

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

 

но если вы начнёте задавать вопросы типа "что такое макрос" - то вам здесь помощи не будет.

 

 

Неужели я здесь так тупо "выгляжу" ? Если я что-то не знаю я лезу в нэт и изучаю, по возможности проверяю, а потом уже пытаюсь применить к симу. И только после, если ничего не выходит, обращаюсь к Вам, профессионалам...

Share this post


Link to post
Share on other sites
serg_p
... Неужели я здесь так тупо "выгляжу" ? ...

Не, по мне - не тупо. Вы, вроде, тот человек, которому не грех и помочь, по возможности естественно.

 

Тут я давно уже решил для себя сделать не только прибор в отдельно компилированном модуле, но и выключатели. И проще искать их будет для редактирования и перекомпилировать быстрее. Может я и не совсем прав...

Переключатели - тоже приборы. Хоть они и просты, но они, как и все приборы начинаются с GAUGE_HEADER_FS... + у них есть один STATIC и один ICON.

Да, так вполне можно. И даже, наверное, именно так и стоит делать. Тут налицо пару плюсов:

1. Легко найти. Достаточно покрутить список cpp в проекте.

2. Легко удалить, не трогая ничего больше. Просто удаляешь файл и строку с extern объявленим хедера этого прибора.

 

 

P.S.

В последствии стоит поделиться с Вами тем, как развязать уровень логики и уровень представления. Как стоит программировать, что бы это получилось и что для этого нужно использовать. Для FSX я приводил здесь пример, как крутить уровень логики независимо от уровня представления. Там используется штатная пописка на событие сима через SimConnect. Хороший пинок для этого дал мне Денис FortRoss :D Для FS9 поделюсь позже, если у Вас возникнет потребность. Тут будут нужны некоторые заголовочные файлы, которые ходят только по рукам. Ну и объяснить надо. Как нужно будет - пишите в личку. Это очень важный вопрос. Я сейчас всю логику в том числе и TCAS и прибора озвучки и ККП в ПТ Ту-154М повесил на недокументированное событие сима (естественно пришлось поколбаситься с переделкой кода). Теперь все начинает работать вне зависимости от вида с которым загрузился. Т.е. уровень логики М-ки теперь независим от уровня представления.

Edited by serg_p

Share this post


Link to post
Share on other sites
seyco

Не, по мне - не тупо. Вы, вроде, тот человек, которому не грех и помочь, по возможности естественно.

 

В последствии стоит поделиться с Вами тем, как развязать уровень логики и уровень представления...

 

Спасибо Сергей, я очень признателен Вам. Меня тоже Сергей зовут (не понял где и как при регистрации это можно указать, позже разберусь как). Я на форум очень редко обращаюсь, в основном приходится разбираться самому. Читаю, вникаю. Я из тех, кто хочет сделать всю модель своими руками. Хоть и говорят, что это не реально, а я делаю... Конечно долго, но это в удовольствие. Сейчас летом времени меньше уделяю, много дел на участке + застройка, ремонт,(живу за городом), + авиамоделированием занимаюсь. А как снег, времени свободного навалом... На данный момент визуальная часть модели готова + звуки (ушло 4 месяца). Модель большая, какая - пока секрет. Немного поковырял динамику, без приборов ее конечно не отладить. Теперь занимаюсь приборами...

Edited by seyco

Share this post


Link to post
Share on other sites
icebear

Адрюх, ты, похоже - весь в Java.

 

Отдельно компилируемый модуль - это один cpp с реализаций и один h с интерфейсом (в результате компиляции, не сборки, а именно копиляции, получается отдельный obj и он, если cpp не трогали и будет просто браться компановщиком в следующий раз - вот раздельная компиляция модулей).

 

Ах ты ж вот оно что! Значит у нас всё в шоколаде. :D

Share this post


Link to post
Share on other sites
icebear

Неужели я здесь так тупо "выгляжу" ? Если я что-то не знаю я лезу в нэт и изучаю, по возможности проверяю, а потом уже пытаюсь применить к симу. И только после, если ничего не выходит, обращаюсь к Вам, профессионалам...

 

Ну, я даже такого и не намекал. Здесь Вам помогут по конкретной проблеме, по возможности конечно. Тем более здесь есть реально профессиональные люди, твёрдо стоящие обоими ногами как в С так и в симе. Но с другой стороны ни у кого нет желания кого-либо другого учить азам С, ибо это муторно и занимает много времени. Были прецеденты, да ещё и претензиями.

Share this post


Link to post
Share on other sites
serg_p

Да, были. Было видно, что человек откровенно далек, ну просто совсем. Но почему-то он считал, что ему тут обязаны вдолбить в его голову все базовые понятия. В конце-концов дело заканчивалось тем, что он уходил сказав , что мы тут зазнавшиеся козлы, ну типа этого. И такое было не раз. Были так же и защитники, которые абсолютно ничего не понимали, а так же абсолютно не понимали, что и по чем, но почему-то защищали.

 

Конечно, Сергей, объем Вы себе выбрали огроменный. Правда есть пример. Никита. Автор Ту-144. Он сделал и 3D модель и все нарисовал и динамику сделал и приборы. Но приборы такой сложности на XML - это кошмар. Это работает, но это кошмар. Так делать не стоит. По моему мнению. Сложные вещи для сима должны программироваться на C/C++. Другого средства нет. Был бы в симе, например, lua или еще какой-нибудь процедурный язык для создания приборов - было бы другое дело. Но на XML делать хорошие приборы нельзя.

 

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

Edited by serg_p

Share this post


Link to post
Share on other sites
seyco

Попробую выжать максимум из того, что знаю и еще узнаю, это хобби, а хобби не должно быть рутиной. В ХМЛ я сделал некоторую анимацию, там не сложно, но вот для приборов ХМЛ меня испугал, и я для себя решил, что только С++, тем более опыт в ассемблере есть.

Share this post


Link to post
Share on other sites
BoL4oNoK

Простите если не в теме, но никак не могу найти, как получить значение L переменной?

Share this post


Link to post
Share on other sites
seyco

К сожалению я пока не дополз до L-переменных, сразу во все не лезу, иначе котелок закипит, хотя близко к этому... Это все-таки первый самолет мой.

Мне бы, кстати, в будущем тоже пригодилось бы эта информация.

Edited by seyco

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...