IMHO программа должна достаточно легко читаться, если клиент будет взаимодействовать с модулем календаря только через этот интерфейс (само собой, подправить в случае хранения строк во FLASH/EPROM).
Подскажите пожалуйста, в Си считается нормальным когда функция внешние переменные меняет или этого лучше избегать? Я пока что через указатели передаю.
Написал Понг, хочется сделать код поаккуратней.
Бывает, что многовато в результате действия функции должно поменятся, ну хотя бы функция начинающая новую игру, должен сброситься счёт у обоих игроков, координаты ракеток, направление мячика по X и по Y . Как правильно делать?
Для начала я бы постарался избавиться от void* в пользу более строгого типа. Если Next, Parent и Child - это соответственно следующий, родительский и дочерний элементы меню, то и указывать следует на них, а не на бесформенную область памяти. Если я не угадал и это другие объекты, то все равно можно подобрать для них более подходящий тип.
И саму структуру лучше бы спрятать от клиента, ему вовсе не обязательно знать ее внутреннее строение.
Еще не понял назначение конструкций вроде extern menuItem Next. Зачем понадобилось делать Next доступным глобально?
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Подскажите пожалуйста, в Си считается нормальным когда функция внешние переменные меняет или этого лучше избегать?
С функциями с побочным эффектом ситуация примерно такая же, как с оператором goto: если есть возможность, лучше его избежать, но изредка бывают случаи, когда они полезны ("изредка" - это в исключительных случаях, а не через каждые 20 строк). Если есть возможность, однозначно лучше избежать.
brian4ever писал(а):
Бывает, что многовато в результате действия функции должно поменятся, ну хотя бы функция начинающая новую игру, должен сброситься счёт у обоих игроков, координаты ракеток, направление мячика по X и по Y . Как правильно делать?
Правильно/неправильно - это скорее тема для холивара, у любого клуджа всегда найдется адвокат, с пеной у рта утверждающий: "зато работает!", и переубедить его невозможно, да и незачем в общем. Имеет смысл говорить о плюсах и минусах каждого варианта.
Лично я завел бы для хранения текущего состояния игры отдельный модуль, в котором инкапсулированы все переменные. Модуль должен иметь метод инициализации, который устанавливает начальные значения всех переменных. При таком подходе в случае необходимости можно добавить впоследствии еще переменные состояния, и имеющийся код клиента переписывать не придется.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Для начала я бы постарался избавиться от void* в пользу более строгого типа. Если Next, Parent и Child - это соответственно следующий, родительский и дочерний элементы меню, то и указывать следует на них, а не на бесформенную область памяти. Если я не угадал и это другие объекты, то все равно можно подобрать для них более подходящий тип.
Можете подсказать хорошую книжку про Си ? Особенно про построения макросов.. Я как бывший С# программист привык к удобным конструкциям из С#.
Мой код - развитие MicroMenu из Avrfreaks. Собственно (*void) наследование - по другуму не знаю как делать макрос такого вида. Указатель конечно на тот же тип как и само мену.
Что имеете под видом клиента ? Наружу имеют доступ только событию меню (минус, плюс, ввод, меню.)
Запасной вариант - сделать программу для генераций menu.h из удобного хмл файла..
Лично я завел бы для хранения текущего состояния игры отдельный модуль, в котором инкапсулированы все переменные. Модуль должен иметь метод инициализации, который устанавливает начальные значения всех переменных. При таком подходе в случае необходимости можно добавить впоследствии еще переменные состояния, и имеющийся код клиента переписывать не придется.
А есть способ попроще, который можно порекомендовать начинающему?
Самые начальные буквари советовать не буду, их навалом и практически все равноценны, поскольку толкут одну и ту же воду в одинаковых ступах; если программировали на куда более развитом C#, то разобраться с C легко сможете по любому попавшемуся под руку справочнику.
Примеры неплохого встроенного кода можете посмотреть в Jean J. Labrosse. Embedded Systems Building Blocks. Правда, насколько я помню, Лабросс пишет для интеловского микропроцессора (кажется, i80186 или что-то наподобие), но на то он и C, чтобы не заморачиваться на подобные мелочи.
Ну и, конечно же, незаменимая "книжка" по C - стандарт языка. Например, ISO/IEC 9899.
Staska писал(а):
Я как бывший С# программист привык к удобным конструкциям из С#.
Отличная основа. Наверняка как минимум знакомы с основными паттернами проектирования и имеете практику объектно-ориентированного анализа. А реализовать хороший проект сможете без труда и на обычном C.
Staska писал(а):
Собственно (*void) наследование - по другуму не знаю как делать макрос такого вида. Указатель конечно на тот же тип как и само мену.
Классический вариант - элемент структуры ссылается на саму структуру:
TreeNode * вместо аморфного void * позволяет использовать все преимущества сильной типизации.
Staska писал(а):
Что имеете под видом клиента ?
Клиент некоторого модуля - это любой код, использующий ресурсы этого модуля. В нашем случае - код, работающий с меню (например, отображающий его визуальное представление).
Staska писал(а):
Наружу имеют доступ только событию меню (минус, плюс, ввод, меню.)
А как быть с объявлениями extern? Они ведь имеют внешнюю компоновку и, следовательно, нарушают инкапсуляцию.
Staska писал(а):
Запасной вариант - сделать программу для генераций menu.h из удобного хмл файла..
Хорошая идея. Но с генераторами кода связана общая проблема: если в сгенерированный код внесены изменения, а потом понадобилось перегенерировать его заново (например, добавились пункты меню), все изменения потеряются.
Нужно продумать очень хороший, самодостаточный генератор, к выходу которого ничего не нужно приписывать вручную.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
А есть способ попроще, который можно порекомендовать начинающему?
Этот способ на самом деле вовсе не сложен, начинающие при желании могут его легко постичь. А пользоваться таким модулем будет намного проще, чем простым набором переменных, поскольку он защищен от многих случайных ошибок кодирования.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
А как быть с объявлениями extern? Они ведь имеют внешнюю компоновку и, следовательно, нарушают инкапсуляцию.
Ошибка.
Цитата:
Хорошая идея. Но с генераторами кода связана общая проблема: если в сгенерированный код внесены изменения, а потом понадобилось перегенерировать его заново (например, добавились пункты меню), все изменения потеряются.
Потому и говорю - запасной вариант. Главное в таком коде - комментарий жирный буквами "Auto-generated code, do not change" И все таки - хочется сделать все одним куском. Но удобно. Оставим меню на потом, оно и так работает, нужно будет ethernet в основной плате добить.
Вот, кстати, еще рекомендация по улучшению (хоть и относится не к вопросам по C, а к самой задаче).
Предлагаю убрать из структуры поле *Parent. Вместо этого каждый раз, спускаясь на следующий уровень меню, откладывать текущий уровень в стек. Тогда для возврата к предыдущему уровню достаточно лишь снять указатель с верхушки стека.
Плюсы. Во-первых, исключена ошибка, когда из-за невнимательности кодера элемент меню ссылается на чужого родителя. Достаточно правильно определить дочерние элементы.
Во-вторых, проще модифицировать меню (например, переносить опции из одного подменю в другое.
В-третьих, небольшая экономия памяти, если меню может строиться динамически в ОЗУ. Глубина вложения меню вряд ли превысит 3-4 уровня, соответственно и стек потребуется небольшой.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Вот, кстати, еще рекомендация по улучшению (хоть и относится не к вопросам по C, а к самой задаче).
Предлагаю убрать из структуры поле *Parent. Вместо этого каждый раз, спускаясь на следующий уровень меню, откладывать текущий уровень в стек. Тогда для возврата к предыдущему уровню достаточно лишь снять указатель с верхушки стека.
Экономия будет по стороне флеша, но не по стороне рама. Мы убираем один байт из каждого меню, но должны добавить с 1+макс_глубина_меню переменных.
Последний раз редактировалось Staska Пт фев 22, 2013 11:08:01, всего редактировалось 2 раз(а).
Экономия будет по стороне флеша, но не по стороне рама
Это при условии, что меню жестко прошивается во FLASH и всегда неизменно. Впрочем, первые два плюса гораздо существеннее этой мизерной экономии, если только не идет борьба за каждый байт (в случае, если кристалл выбран неправильно), поскольку исключают потенциальный источник ошибок.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Экономия будет по стороне флеша, но не по стороне рама
Это при условии, что меню жестко прошивается во FLASH и всегда неизменно. Впрочем, первые два плюса гораздо существеннее этой мизерной экономии, если только не идет борьба за каждый байт (в случае, если кристалл выбран неправильно), поскольку исключают потенциальный источник ошибок.
Это я из испуга. Запускал недавно tcp/ip на меге 328 - каждый байт считаеш. Зная что так не делается - но теперь для сети нужен просто дополнительный вывод на разъем программатора. И если уж менят процессор - то только на кортех.
Я пункты меню делал в массиве во флеш, который по мере надобности копируется в ОЗУ. Точно так же ссылки на всех 4 родственников (выбор пунктов меню реализован несколько коряво но не о нем речь). Преимущество перед размазыванием по памяти - на потомка надо тратить 1 байт (адрес в массиве) а не 2 (а то и больше, адрес во всей памяти).
Вот. в общем на io_interface(EXT_READ_NVRAM,pNVRam); пишет "унхандлед эксепшн access violation reading location 0xffffffff" Кароче я сам ничего не понял. Толи память не выделилась. Толи указатель кривой, может вообще ченить другое. Мне самое главное понять имено вот это выражение typedef void* (*_ext_Interface)(int, void*). Это типа указатель на указатель?
Вообще это эмулятор, просто хотел припрутить междумордие, заодно нормально подучить язык до СПП. Кто мчто может подскажет, и разжует по порядку?
Мне самое главное понять имено вот это выражение typedef void* (*_ext_Interface)(int, void*). Это типа указатель на указатель?
Больше похоже на указатель на функцию от двух аргументов (int и void*), возвращающую void*.
Во время компиляции никаких предупреждений не было?
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Мне самое главное понять имено вот это выражение typedef void* (*_ext_Interface)(int, void*). Это типа указатель на указатель?
Больше похоже на указатель на функцию от двух аргументов (int и void*), возвращающую void*.
Во время компиляции никаких предупреждений не было?
В принципе только не явная конверсия типов(или как там?). Вот полный листинг(на всякий случай)
Код:
1>------ Rebuild All started: Project: _device_sys, Configuration: Debug Win32 ------ 1>Build started 23.02.2013 21:17:12. 1>_PrepareForClean: 1> Deleting file "Debug\_device_sys.lastbuildstate". 1>InitializeBuildStatus: 1> Creating "Debug\_device_sys.unsuccessfulbuild" because "AlwaysCreate" was specified. 1>ClCompile: 1> _device_sys.cpp 1> XBUS.cpp 1> vdlp.cpp 1> PLD3.cpp 1> quarz.cpp 1> PLD2.cpp 1>PLD2.cpp(489): warning C4005: 'CHAR_BIT' : macro redefinition 1> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\limits.h(23) : see previous definition of 'CHAR_BIT' 1>PLD2.cpp(702): warning C4244: '=' : conversion from '__int64' to 'double', possible loss of data 1> Iso.cpp 1> DSP.cpp 1> DiagPort.cpp 1> PLD1.cpp 1> bitop.cpp 1> arm.cpp 1> Generating Code... 1>Manifest: 1> Deleting file "Debug\_device_sys.exe.embed.manifest". 1>LinkEmbedManifest: 1> _device_sys.vcxproj -> P:\Testapp\test\Debug\_device_sys.exe 1>FinalizeBuildStatus: 1> Deleting file "Debug\_device_sys.unsuccessfulbuild". 1> Touching "Debug\_device_sys.lastbuildstate". 1> 1>Build succeeded. 1> 1>Time Elapsed 00:00:09.06 ========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
Создал тупо Win32 console application из файла device_sys.cpp, с помощью мастера(create project from exiting source) дабы проверить вообще возможность сборки
Попробуйте поставить точку останова в строке с нежным именем //ЖОПА и поглядеть, чему там равно значение переменной io_interface. Возможно, она (переменная) неправильно инициализирована.
_________________ Любой дурак может писать код. Настоящий профессионал - это тот, кто способен постоянно создавать продукт высокого качества, укладываясь при этом в бюджет. J. Ganssle
Попробуйте поставить точку останова в строке с нежным именем //ЖОПА и поглядеть, чему там равно значение переменной io_interface. Возможно, она (переменная) неправильно инициализирована.
имя значение тип io_interface 0x006c0040 void * (int, void *)*
Да наверняка. Но проблема что тот кусок кода я не трогал, сами исходники рабочие, программа работает под виндой, но очень медленно, а программисты свалили втихую, и помогать не собираются никому и низа что. Хочу для начала прилепить интерфейс, в перспективе паралелизацию. Но пока познания мои сводятся к тому что, когда я вижу такое обилие символов "*" мозг пытается свалить из черепушки под диван
Аlex писал(а):
Цитата:
Больше похоже на указатель на функцию от двух аргументов (int и void*), возвращающую void*.
Немножко не так. Объявляется тип _ext_Interface как функция, принимающая 2 аргумента (int и void*) и возвращающая void*.
Вот и хочется понять что-же это void*. С моими познаниями в Си - void это отсутствие аргументов, короче пустота. А тут указатель на нее, причем указатель на указатель пустоты.
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения