sensor_ua 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба Попробуй таки найти текстовым поиском "#define IN" и "#define OUT" или "отбросить" их как нечто ненужное;) #define IN #define OUT у меня, например, в кейле используется #define __flash чтобы код из IAR не трогать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба Попробуй таки найти текстовым поиском "#define IN" и "#define OUT" или "отбросить" их как нечто ненужное;) #define IN #define OUT у меня, например, в кейле используется #define __flash чтобы код из IAR не трогать А вот на такое безобразие препроцессор сругаться должен. Вообще-то #undef для этих целей существует ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sensor_ua 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба А вот на такое безобразие препроцессор сругаться должен Вот и найдётся;) А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить. Сложнее переопределить чего-нить типа typedef enum bool{ false = 0, true = -1 }; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить.Дык #ifdef __flash #undef __flash #endif и всех делов :) Сложнее переопределить чего-нить типаВот именно что "сложнее", потому и надежнее. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sensor_ua 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба Гы;) Мне код перебивать не нужно. Мне как раз единожды написанное под яром нужно не править - у меня текст один для кейла и яра, а ненужные квалификаторы просто выбрасываются после разруливания;) потому и надежнее. Тут в примере особо изощренный случай. Такое "надёжное" лучше бы не попадалось вовсе. Пример наглого использования умолчаний Си bool is_leap(int year){ if(!(year%400))return true; if(!(year%100))return false; if(!(year%4))return true; return false; } int Get_Days_In_Month(int month, int year){ return(Days_Array[month]+(month==2)?is_leap(year):0); } при таких объявлениях bool просто веселит:) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Lem 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба препроцессором не рекомендуется пользоваться именно из-за проблем с поиском ошибок (в основном), так как во что разворачивается макроопределение выяснить практически нереально (достоверно), отлаживаться и искать ошибки крайне неудобно. Поэтому были введены в язык средства, позволяющие практически полностью исключить использование препроцессора. Список случаев, когда препроцессор оправдан уже был озвучен. Правда, я лично использую его неправильно, надо отучаться... Для маленьких проектов, например, для микроконтроллеров типа АВР, разницы никакой, но привычка переносится и на большие, а там проявляются все эти кажущиеся надуманными проблемы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sensor_ua 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба препроцессором не рекомендуется пользоваться Простите, но кем и где не рекомендуется? В стандарте? во что разворачивается макроопределение выяснить практически нереально (достоверно) ИМХО, это больше зависит от стиля написания и принятого механизма проверок. ЗЫ Во встраиваемых устройствах до недавнего времени довольно часто приходилось экономить ОЗУ при достаточном объёме памяти программ. Грамотное использование препроцессора позволяет более рационально использовать память программ, высвобождая ОЗУ. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 31 марта, 2008 Опубликовано 31 марта, 2008 · Жалоба Так там рекомендуется использовать препроцессор только тогда, когда это необходимо. Объясняется все просто: код, активно использующий препроцессор сложно читать, сопровождать и отлавливать ошибки. Пишем, пишем... Объявляем трехэтажный мат #define func_type_macro(arg1,arg2,arg3) ..... Применяем объявленный трехэтажный в 10-ти этажной конструкции нужное количество раз. В куске кода, размер которого никого не испугает. .......................................... Отменяем #undef func_type_macro В общем, я для себя решил, что если у меня накапливаются глобальные сложные дефайны, то это значит, что реализация идеи неправильная. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба Просветите, плз, откуда такая категоричная постановка? Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском. Вкратце. Макроподстановка таит в себе опасность из-за того, что делается тупо, без контроля типов и контекста, в котром она производится. При этом пользователь видит одно, а получает другое. Наглядный пример тому - соседняя ветка про вычисления в макросах, когда пара лишних скобок меняла контекст выражения и вычисление выражения с этапа компиляции переносилось на этап выполнения программы, что предъявляло совершенно другие требования по потреблению стека, в частности. Причем все это происходит скрытно от пользователя, в этом и подлость. Отсутствие анализа контекста несет еще косяки - недавно обсуждали: например, фирма IAR ввела для некоторых новых чипов AVR макроопределения в заголовочных файлах ioxxx.h типа #define N 2 #define C 0 Теперь, если пользователь использует где-то внутри, в недрах функций (да хоть в списке аргументов прототипов) простое короткое имя N или C, то все, привет. Причем, в ряде случаев весьма неочевидно, в чем ошибка. И хорошо, если еще компилятор заругается на что-то, а то ведь может и прокатить компиляция, а в железе не работает. Вот тут попляшешь с бубном. А все потому, что препроцессор работает тупо и кладет на пространства имен, контекст выражения и делает свою работу скрытно. Некоторое время назад попался прикол (возможно байка, но все равно забавно): в исходниках, оставшихся от недавно уволенного программиста после долгой и болезненной отладки обнаужили такую сточку в одном из заголовочных файлов: #define TRUE FALSE // счастливой отладки, суки Список примеров можно продолжать, то повторять не хочется, уже обсуждалось не раз. Короче, макроподстановки могут приводить и приводят к труднообнаруживаемым, подлым ошибкам и несут неконтролируемое (и часто слабопредсказуемое) поведение. По этим причинам использование макроподстановок должно быть минимизировано до уровня минимально необходимого, без которого реально не обойтись. Сюда относятся простые макросы, без сложных выражений, задающие порядок условной компиляции, и другие простые замены, часто используемые при портировании. Это практически все, никаких констант, никаких макрофункций быть не должно. Почему-то приходится применять гораздо шире. Где не вижу тонкой грани? А вот мне не приходится. И все получается. Приходится применять только в рамках вышеописанного - для условной компиляции и простых подстановок при портировании. Псевдонимы типов прекрасно решаются с помощью typedef, о чем уже сказали. Макросы-литералы замечательно заменяются настоящими типизированными константами. При этом никакого оверхеда не возникает - если нет попытки взять адрес константного объекта или завести ссылку на него, то компилятор не будет его размещать в памяти. Правда, это относится к С++, где у констант по умолчанию внутреннее связывание. Но, думается, сегодня уже нет проблем использовать плюсатый компилятор. В случае с С для получения такого же поведения константные объекты придется обзывать дополнительно static. Макросы-функции без проблем заменяются встраиваемыми (inline) функциями. Это на сегодняшний день штатно умеют уже не только С++, но и С компиляторы. По Стандарту. Если требуются какие-то нетривиальные вычисления и надо, чтобы они гарантировано производились на этапе компиляции (во избежание оверхеда), то существует мнение (и я его придерживаюсь), что такие вещи лучше вообще выполнять внешними инструментами и подсовывать компилятору уже готовые значения (потому как компилятор - не калькулятор). Через опцию -D, либо вообще поместив их в отдельный файл, который включается в проект. При использовании систем сборки (make, scons) технических трудностей не возникает никаких. Да и с оболочками тоже проблем во многих случаях нет. В любом случае не слишком разумно заставлять пыжиться целочисленный препроцессор с его 32-битной арифметикой - он отомстит (что часто и происходит). :) Если все это вас не убедило, поищите в сети мнение авторитетного дяденьки Б.Страуструпа (автора языка С++), возможно, он подробнее объяснит. :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Axxel 0 1 апреля, 2008 Опубликовано 1 апреля, 2008 (изменено) · Жалоба ПОКАЖИ typedef struct TYPE{ ...... ...... ...... } TYPE TYPE var; ...... Да, конечно, создание структуры созданием нового типа назвать нельзя. Правильнее говорить "структура типа type" вот что я имел ввиду. sKWO спросил, насколько я понял, "что может быть если где-то встречается нстнадартный тип" Изменено 1 апреля, 2008 пользователем Axxel Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sensor_ua 0 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском. Простите, но что искать? По слову макрос? Я тут довольно часто, но мимио видно всё интересное по теме пробежало. Макроподстановка таит в себе опасность Я не спорю, что пользоваться нужно уметь, а тем более таких глупых вещей, как примере от IAR, не допускать (с примерчиком познакомился ранее просматривая хедеры). По этим причинам использование макроподстановок должно быть минимизировано до уровня минимально необходимого, без которого реально не обойтись. А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятные;) варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации приводит к ещё более "подлым" ошибкам, чем жесткое конфигурирование с использованием макроподстановок (ну нет шаблонов в Си). Но, ИМХО, всё это имеет право на использование, если это не самоцель. Макросы-литералы замечательно заменяются настоящими типизированными константами. При этом никакого оверхеда не возникает - если нет попытки взять адрес константного объекта или завести ссылку на него, то компилятор не будет его размещать в памяти. Правда, это относится к С++, где у констант по умолчанию внутреннее связывание. К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная. то существует мнение (и я его придерживаюсь) опять же всё субъективно. Если не понимать ограничений препроцессора, то можно и в самом простом ошибиться. (Я, например, для редких случаев сложных константных выражений применяю тесты на интерпретаторе Ch) Но, думается, сегодня уже нет проблем использовать плюсатый компилятор К сожалению, есть. Некоторое время назад попался прикол (возможно байка Аналогично #define else ; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба Пример winavr eeprom_read_block(&my_variable,&my_param,sizeof(my_variable)); Ну не хочу я стока писАть !!! #define eeread(Var,Param) eeprom_read_block(&Var,&Param,sizeof(Var)) .......................... eeread(my_variable,my_param); Интересует субъективное мнение: оправдано ли в данном случае применение макро? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DogPawlowa 0 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба Простите, но что искать? По слову макрос? Я тут довольно часто, но мимо видно всё интересное по теме пробежало. Я расскажу интересное! :) Не далее как вчера час искал отсутствие фигурных скобок в макросе из двух команд, который использовался в if /else. На фоне какой-то непонятной потери синхронизации с JTAGом повеселился по полной программе. Сам себе клоун. :( Тем не менее сложные макросы использую, вариант с функциями inline не перекрывает всех возможных ситуаций. Использую один раз созданную конструкцию для организации доступа к переменным в протоколе обмена и переношу из проекта в проект, один раз отладив стиснув зубы :) Но создавать такое в каждом проекте - упаси Бог. Для того, чтобы было понятно, о чем разговор, пример текста описания переменных. В поддержку протокола в объект 'system' включаются 4 переменных со своими именами, и функциями, вызываемыми при чтении и записи. Все это разворачивается в массивы строк и указателей на функции. Добавление переменной в протокол происходит за минуту максимум. IMPL_PROLOG(system) IMPL_VARIABLE(system, 0, "HTI 16V hose pumps", IMPL_GET dpr->tx_value=(long)COUNT_ELEMENTS(system_names); return;, IMPL_SET READ_ONLY ) IMPL_VARIABLE(system, 1, "Firmware version", IMPL_GET dpr->name=version; return;, IMPL_SET READ_ONLY ) IMPL_VARIABLE(system, 2, "Objects quantity", IMPL_GET dpr->tx_value=(long)OBJECTS_QUANTITY; return;, IMPL_SET READ_ONLY ) IMPL_VARIABLE(system, 3, "Heartbeat Period,s", IMPL_GET GET_HEARTBEAT, IMPL_SET SET_HEARTBEAT ) IMPL_EPILOG() Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная. Еще добавлю примерчик static PROGMEM char array[3] = {'A','B','C'}; ................................ char a; a = array[0]; Winavr сделает a='A' , поскольку array[0] рассматривается как константное выражение. Сделают ли то же самое компилеры с услужливым сервисом "чтения флеша" - не уверен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 1 апреля, 2008 Опубликовано 1 апреля, 2008 · Жалоба А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятные;) варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации... Нет, это другое. Это вы про элементы низкоуровневого программирования, которые сопряжены необходимостью внимательно следить за соблюдением правил. Но это, повторяю, другое - тут у вас все на виду, анализ кода сразу показывает гнилое место. Сразу видно, где потенциальные грабли - адресная арифметика, операции с указателями, инициализация аргегатных объектов, ручные преобразования типов и т.д., этим вещам надо уделять повышенное внимание. В то время как с макроподстановкой пакостное поведение скрыто от непосредственного взгляда, и негативные последствия могут вылезать совсем в другом месте. К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная. Вернее - это констатный объект. Если нет необходимости размещать его в памяти (не берется адрес и нет ссылки на него), то никакой вменяемый компилятор этого делать не будет - именно для этой возможности в том же С++ специально ввели правило, согласно которому константные объекты имеют внутреннее связывание... Хорошо, не верите, давайте проверим. Поскольку тут форум по AVR, то на нем и будем тренироваться. Код: // file: slon.cpp const int a = 10; int f(int x) { return x + a; } Комилятор - какой-то от IAR. v4.хх. Не суть важно. Результат: const int a = 10; In segment CODE, align 2, keep-with-next int f(int x) ??f: { return x + a; 00000000 5F06 SUBI R16, 246 00000002 4F1F SBCI R17, 255 00000004 9508 RET } Как видим, все замечательно получилось, ничего в памяти не размещается, никаких обращений к памяти, ессно, нет. Это в режиме ++. Отключаем его - в С режиме результат: In segment NEAR_I, align 1, keep-with-next REQUIRE `?<Segment init: NEAR_I>` const int a = 10; a: DS 2 REQUIRE `?<Initializer for a>` In segment CODE, align 2, keep-with-next int f(int x) f: { return x + a; 5F06 SUBI R16, 246 4F1F SBCI R17, 255 9508 RET } In segment NEAR_ID, align 1, keep-with-next `?<Initializer for a>`: 000A DW 10 Да, тут, как и положено, константа 'a' размещена в памяти (и инициализатор тоже съест свою часть в сегменте инициализаторов), потому что компилятор не имеет права этого не сделать - в С константные объекты имеют по умолчанию внешнее связывание и к ним может производиться обращение из других единиц компиляции. В этом случае линкер будет материться на отсутствие объекта. Но даже в этом случае компилятор, видя значение констатнты, не стал городить обращение к ней, а сгенерил более короткий эффективный код. Поведение можно сделать как в ++ варианте, снабдив объявление констатны квалификатором static: static const int a = 10; Результат аналогичен первому варианту: static const int a = 10; In segment CODE, align 2, keep-with-next int f(int x) f: { return x + a; 5F06 SUBI R16, 246 4F1F SBCI R17, 255 9508 RET } Думается, что написать ключевое слово 'static' не является слишком обременительным. Кстати, сделать в С++ поведение констант аналогичным С-шному можно сделать с помощью объявления их как 'extern'. Результат компиляции приводить не буду, поверьте, оно точно соотвествует вышеприведенному С без 'static'. К сожалению, есть. И в чем они состоят? Для AVR хоть коммерческий IAR, хоть безплатный AVR-GCC давно уже поддерживают почти все фичи С++ (за исключением тяжелых, которые реально применять на AVR смысла нет). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться