Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Стиль программирования на Си
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
sKWO
Добрый день!
Столкнулся со следующей проблемой.
нашол код как сделать восемь таймеров написанный Alechin Jan.
Первое что кидается в глаза та это синтаксис мне непонятный.
Вот к примеру прототип функции остановки тай мера:
Код
void Stop_Timer(IN IDX timer);

ну и сама функция:
Код
/*--------------------------------------------------------------------------------------------*/
/* Процедура остановки таймера.                                                               */
/* Принимает:   номер таймера (0 - 7).                                                        */
/* Возвращает:  ничего не возвращает.                                                         */
/*--------------------------------------------------------------------------------------------*/
__monitor void Stop_Timer(IN IDX timer)
{
  Timer[timer].Timer_Ena = TIMER_DIS;
  return;
}

Непонятно что такое IDX? Ну IN понятно, направление, очень удобно.
IDX по ходу здесь беззнаковый чар.
Ну и соответственно вопрос:
Этот синтаксис возможен через какоето макроопределение?
Может кто знает, подскажите, уверен это интерессно не только мне.
Вот ещё пример
Код
void Start_Timer(IN IDX timer, IN WORD interval, IN pTIMER_FUNC pFunc = NULL, IN bool cycle = TIMER_ONCE);

Спасибо.
Axxel
Цитата(sKWO @ Mar 31 2008, 16:23) *
Этот синтаксис возможен через какоето макроопределение?

Да, typedef, или через структуру.
aaarrr
Стиль жуткий, не надо так делать.

Цитата(Axxel @ Mar 31 2008, 14:36) *
Да, или через структуру.

Это как?
defunct
Цитата
Этот синтаксис возможен через какоето макроопределение?

да, и через макроопределение:

Код
#define CHAR char


и через typedef

Код
typedef char CHAR;


Но думаю что конкретно в вашем примере IDX задумывался быть перечислимым типом, объявленным через enum

Код
typedef enum
{
    TIMER1 = 0,
    TIMER2,
    TIMER3,
    .... ,
    TIMER_COUNT
} IDX;
sKWO
Цитата(defunct @ Mar 31 2008, 14:59) *
да, и через макроопределение:

Код
#define CHAR char


и через typedef

Код
typedef char CHAR;


Но думаю что конкретно в вашем примере IDX задумывался быть перечислимым типом, объявленным через enum

Код
typedef enum
{
    TIMER1 = 0,
    TIMER2,
    TIMER3,
    .... ,
    TIMER_COUNT
} IDX;

Ага, спасибо. Чёта мучают сомнения нащёт квалификатора перечисления
Но тогда непонятно это (Его же стиль)
Код
OUT bool Wait_LCD_Ready(IN IDX c)

Да, тогда ещё вопрос :
Если возможности препроцессора ограничены, то лучше использовать typedef используя возможности компилятора? И в каких случаях предпочтительнее #define?
Если кому не тяжело , приведите конкретный пример чтобы компилятор проглотил!
Спасибо!

Цитата(Axxel @ Mar 31 2008, 14:36) *
Да, typedef, или через структуру.

ПОКАЖИ

Цитата(aaarrr @ Mar 31 2008, 14:40) *
Стиль жуткий, не надо так делать.

Возможно, но мне просто интерессно. Просто в голове не укладывается как ?!
dxp
Цитата(sKWO @ Mar 31 2008, 18:56) *
Да, тогда ещё вопрос :
Если возможности препроцессора ограничены, то лучше использовать typedef используя возможности компилятора? И в каких случаях предпочтительнее #define?

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

Просветите, плз, откуда такая категоричная постановка?
Цитата
По большому счету препроцессор имеет смысл использовать только для реализации условной компиляции да простеньких макроподстановок типа вставки платформенно-зависимых квалификаторов.

Почему-то приходится применять гораздо шире. Где не вижу тонкой грани?
Alechin
Так как кусок кода мой - скажу как это описано у меня (то бишь в оригинале):


// Тип переменной, занимающей 1 байт (0..255).
typedef unsigned char BYTE;
#define MAX_BYTE ((BYTE)-1)

// Тип переменной-указателя на переменную, занимающую байт.
typedef BYTE * pBYTE;

//--------------------------------------------------------------------------------------------
// Тип индексной переменной (0..255);
typedef unsigned char IDX;
#define BAD_IDX ((IDX)-1)

//--------------------------------------------------------------------------------------------
// Тип переменной, занимающей 2 байта (0..65536).
typedef unsigned short WORD;
#define MAX_WORD ((WORD)-1)

// Макрокоманда получения значения младшего байта слова.
#define LO_BYTE(word) (BYTE)(word)

// Макрокоманда получения значения старшего байта слова.
#define HI_BYTE(word) (BYTE)((WORD)(word) >> 8)

// Макрокоманда формирования значения типа WORD из двух значений типа BYTE.
#define MAKE_WORD(lo, hi) (WORD)(((BYTE)(lo)) | ((WORD)(hi) << 8))

// Тип переменной-указателя на переменную, занимающую 2 байта.
typedef WORD * pWORD;

//--------------------------------------------------------------------------------------------
// Тип переменной, занимающей 4 байта (0..4 294 967 296).
typedef unsigned long int DWORD;

// Тип переменной-указателя на переменную, занимающую 4 байта.
typedef DWORD * pDWORD;

// Макрокоманда получения значения младшего слова двойного слова.
#define LO_WORD(dword) (WORD)(dword)

// Макрокоманда получения значения старшего слова двойного слова.
#define HI_WORD(dword) (WORD)((DWORD)(dword) >> 16)

// Макрокоманда формирования значения типа DWORD из двух значений типа WORD.
#define MAKE_DWORD(lo, hi) (DWORD)(((WORD)(lo)) | ((DWORD)(hi) << 16))

//--------------------------------------------------------------------------------------------
// Тип переменной, занимающей 8 байт.
typedef unsigned long long QWORD;

// Макрокоманда получения значения младшего двойного слова четверного слова.
#define LO_DWORD(qword) (DWORD)(qword)

// Макрокоманда получения значения старшего двойного слова четверного слова.
#define HI_DWORD(qword) (DWORD)((QWORD)(qword) >> 32)

// Макрокоманда формирования значения типа QWORD из двух значений типа DWORD.
#define MAKE_QWORD(lo, hi) (QWORD)(((DWORD)(lo)) | ((QWORD)(hi) << 32))

// Тип переменной-указателя на переменную, занимающую 8 байт.
typedef QWORD * pQWORD;

//--------------------------------------------------------------------------------------------
// Тип переменной-счетчика секунд.
typedef DWORD SECONDS_CNTR;
rezident
Извиняюсь, что вклиниваюсь.
Цитата(sensor_ua @ Mar 31 2008, 18:48) *
Просветите, плз, откуда такая категоричная постановка?
Для переносимости кода. Потому, что компиляторы пишут обычно как можно ближе к стандарту языка программирования. А препроцессоры по большей части не стандартизованы (исключая может быть лишь #define и #include). И то, что работает на одном, не обязательно поймет препроцессор другого компилятора. Кроме того, препроцессор не проверяет типы данных и соответственно, оперируя его командами, можно допустить такие ошибки, которые потом "вылавливать" дольше и сложнее.
defunct
Цитата(sKWO @ Mar 31 2008, 13:56) *
Чёта мучают сомнения нащёт квалификатора перечисления
Но тогда непонятно это (Его же стиль)
Код
OUT bool Wait_LCD_Ready(IN IDX c)

Правильно мучают.
Стиль действительно странный, не упирайтесь в него. Попробуйте переписать.

Для арифметики гораздо удобнее объявить типы с короткими именами, и которые будут говорить сами за себя:

Код
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned long U32;
typedef unsigned long long U64;

typedef signed char S8;
typedef signed short S16;
typedef signed long S32;

typedef volatile unsigned char V8;
typedef volatile unsigned short V16;
typedef volatile unsigned long V32;
sensor_ua
Цитата
Для переносимости кода.
...
IMNHO, совсем неубедительно. Именно перепроцессором разруливаю проекты на AVR (IAR/GCC) и LPC (RVDMK/IAR), имеющие общий API. Полезные макросы пишутся, естественно, так, чтобы портировалось корректно, очень многие функции параметризуются макроопределениями по шаблонам. На работе препроцессора завязано очень много, при этом несовместимости как-то не наблюдаетсяwink.gif
rezident
Цитата(sensor_ua @ Mar 31 2008, 20:39) *
IMNHO, совсем неубедительно. Именно перепроцессором разруливаю проекты на AVR (IAR/GCC) и LPC (RVDMK/IAR), имеющие общий API. Полезные макросы пишутся, естественно, так, чтобы портировалось корректно, очень многие функции параметризуются макроопределениями по шаблонам. На работе препроцессора завязано очень много, при этом несовместимости как-то не наблюдаетсяwink.gif
Уже были и неоднократно пояснения почему typedef предпочтительнее #define. Пользуйтесь поиском. Вот, например, одна из недавних веток обсуждения http://electronix.ru/forum/index.php?showt...&hl=typedef
vshemm
Есть замечательная книга Алена Голуба "Правила программирования на С\С++". Небольшая, где на доходчивых примерах описываются разного рода "тонкости" (авторские). Так там рекомендуется использовать препроцессор только тогда, когда это необходимо. Объясняется все просто: код, активно использующий препроцессор сложно читать, сопровождать и отлавливать ошибки. И, имхо, с этим трудно не согласиться smile.gif
sensor_ua
Цитата
Уже были и неоднократно пояснения почему typedef предпочтительнее #define

Спасибо, но я неплохо понимаю, что такое typedef, ну и когда может быть полезно применить #define. Если рассматривать только typedef и #define, то в основном соглашусь с Вами. Только какое это имеет отношение к переносимости кода?

Цитата
Объясняется все просто: код, активно использующий препроцессор сложно читать, сопровождать и отлавливать ошибки

Если использование макроопределений, скажем, явно избыточно, не используются правила образования имен, то соглашусь. Также замечу, что без достойного инструмента, позволяющего "ходить" по коду не только по функциям и переменным, но и по макроопределениям, отлаживаться также может быть затруднительно. Но, если нотации определены и их придерживаются, а инструмент заточен, то всё путём.
sKWO
Спасибо, Ребята! Дисскусия разворачивается хорошая, ну и огромное спасибо автору кода!
Правда нету определения in и out.
sensor_ua
Попробуй таки найти текстовым поиском "#define IN" и "#define OUT"
или "отбросить" их как нечто ненужноеwink.gif
#define IN
#define OUT
у меня, например, в кейле используется
#define __flash
чтобы код из IAR не трогать
rezident
Цитата(sensor_ua @ Mar 31 2008, 23:10) *
Попробуй таки найти текстовым поиском "#define IN" и "#define OUT"
или "отбросить" их как нечто ненужноеwink.gif
#define IN
#define OUT
у меня, например, в кейле используется
#define __flash
чтобы код из IAR не трогать

А вот на такое безобразие препроцессор сругаться должен. Вообще-то #undef для этих целей существует wink.gif
sensor_ua
Цитата
А вот на такое безобразие препроцессор сругаться должен

Вот и найдётсяwink.gif
А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить. Сложнее переопределить чего-нить типа
typedef enum bool{
false = 0,
true = -1
};
rezident
Цитата(sensor_ua @ Mar 31 2008, 23:26) *
А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить.
Дык
Код
#ifdef __flash
  #undef __flash
#endif

и всех делов smile.gif
Цитата(sensor_ua @ Mar 31 2008, 23:26) *
Сложнее переопределить чего-нить типа
Вот именно что "сложнее", потому и надежнее.
sensor_ua
Гыwink.gif Мне код перебивать не нужно. Мне как раз единожды написанное под яром нужно не править - у меня текст один для кейла и яра, а ненужные квалификаторы просто выбрасываются после разруливанияwink.gif
Цитата
потому и надежнее.

Тут в примере особо изощренный случай. Такое "надёжное" лучше бы не попадалось вовсе. Пример наглого использования умолчаний Си
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 просто веселитsmile.gif
Lem
препроцессором не рекомендуется пользоваться именно из-за проблем с поиском ошибок (в основном), так как во что разворачивается макроопределение выяснить практически нереально (достоверно), отлаживаться и искать ошибки крайне неудобно. Поэтому были введены в язык средства, позволяющие практически полностью исключить использование препроцессора.
Список случаев, когда препроцессор оправдан уже был озвучен.

Правда, я лично использую его неправильно, надо отучаться...
Для маленьких проектов, например, для микроконтроллеров типа АВР, разницы никакой, но привычка переносится и на большие, а там проявляются все эти кажущиеся надуманными проблемы.
sensor_ua
Цитата
препроцессором не рекомендуется пользоваться

Простите, но кем и где не рекомендуется? В стандарте?
Цитата
во что разворачивается макроопределение выяснить практически нереально (достоверно)

ИМХО, это больше зависит от стиля написания и принятого механизма проверок.
ЗЫ Во встраиваемых устройствах до недавнего времени довольно часто приходилось экономить ОЗУ при достаточном объёме памяти программ. Грамотное использование препроцессора позволяет более рационально использовать память программ, высвобождая ОЗУ.
_Pasha
Цитата(vshemm @ Mar 31 2008, 19:08) *
Так там рекомендуется использовать препроцессор только тогда, когда это необходимо. Объясняется все просто: код, активно использующий препроцессор сложно читать, сопровождать и отлавливать ошибки.


Пишем, пишем...
Объявляем трехэтажный мат #define func_type_macro(arg1,arg2,arg3) .....
Применяем объявленный трехэтажный в 10-ти этажной конструкции нужное количество раз. В куске кода, размер которого никого не испугает.
..........................................
Отменяем #undef func_type_macro

В общем, я для себя решил, что если у меня накапливаются глобальные сложные дефайны, то это значит, что реализация идеи неправильная.
dxp
Цитата(sensor_ua @ Mar 31 2008, 19:48) *
Просветите, плз, откуда такая категоричная постановка?

Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском.

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

Отсутствие анализа контекста несет еще косяки - недавно обсуждали: например, фирма IAR ввела для некоторых новых чипов AVR макроопределения в заголовочных файлах ioxxx.h типа

Код
#define N 2
#define C 0


Теперь, если пользователь использует где-то внутри, в недрах функций (да хоть в списке аргументов прототипов) простое короткое имя N или C, то все, привет. Причем, в ряде случаев весьма неочевидно, в чем ошибка. И хорошо, если еще компилятор заругается на что-то, а то ведь может и прокатить компиляция, а в железе не работает. Вот тут попляшешь с бубном. А все потому, что препроцессор работает тупо и кладет на пространства имен, контекст выражения и делает свою работу скрытно.

Некоторое время назад попался прикол (возможно байка, но все равно забавно): в исходниках, оставшихся от недавно уволенного программиста после долгой и болезненной отладки обнаужили такую сточку в одном из заголовочных файлов:

Код
#define TRUE FALSE // счастливой отладки, суки

lol.gif

Список примеров можно продолжать, то повторять не хочется, уже обсуждалось не раз.

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

Цитата(sensor_ua @ Mar 31 2008, 19:48) *
Почему-то приходится применять гораздо шире. Где не вижу тонкой грани?

А вот мне не приходится. И все получается. Приходится применять только в рамках вышеописанного - для условной компиляции и простых подстановок при портировании.

Псевдонимы типов прекрасно решаются с помощью typedef, о чем уже сказали.

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

Макросы-функции без проблем заменяются встраиваемыми (inline) функциями. Это на сегодняшний день штатно умеют уже не только С++, но и С компиляторы. По Стандарту.

Если требуются какие-то нетривиальные вычисления и надо, чтобы они гарантировано производились на этапе компиляции (во избежание оверхеда), то существует мнение (и я его придерживаюсь), что такие вещи лучше вообще выполнять внешними инструментами и подсовывать компилятору уже готовые значения (потому как компилятор - не калькулятор). Через опцию -D, либо вообще поместив их в отдельный файл, который включается в проект. При использовании систем сборки (make, scons) технических трудностей не возникает никаких. Да и с оболочками тоже проблем во многих случаях нет. В любом случае не слишком разумно заставлять пыжиться целочисленный препроцессор с его 32-битной арифметикой - он отомстит (что часто и происходит). smile.gif

Если все это вас не убедило, поищите в сети мнение авторитетного дяденьки Б.Страуструпа (автора языка С++), возможно, он подробнее объяснит. smile.gif
Axxel
Цитата(sKWO @ Mar 31 2008, 17:56) *
ПОКАЖИ


typedef struct TYPE{
......
......
......
} TYPE

TYPE var;
......

Да, конечно, создание структуры созданием нового типа назвать нельзя.
Правильнее говорить "структура типа type" вот что я имел ввиду.
sKWO спросил, насколько я понял, "что может быть если где-то встречается нстнадартный тип"
sensor_ua
Цитата
Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском.

Простите, но что искать? По слову макрос? Я тут довольно часто, но мимио видно всё интересное по теме пробежало.
Цитата
Макроподстановка таит в себе опасность

Я не спорю, что пользоваться нужно уметь, а тем более таких глупых вещей, как примере от IAR, не допускать (с примерчиком познакомился ранее просматривая хедеры).
Цитата
По этим причинам использование макроподстановок должно быть минимизировано до уровня минимально необходимого, без которого реально не обойтись.

А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятныеwink.gif варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации приводит к ещё более "подлым" ошибкам, чем жесткое конфигурирование с использованием макроподстановок (ну нет шаблонов в Си). Но, ИМХО, всё это имеет право на использование, если это не самоцель.
Цитата
Макросы-литералы замечательно заменяются настоящими типизированными константами. При этом никакого оверхеда не возникает - если нет попытки взять адрес константного объекта или завести ссылку на него, то компилятор не будет его размещать в памяти. Правда, это относится к С++, где у констант по умолчанию внутреннее связывание.

К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная.
Цитата
то существует мнение (и я его придерживаюсь)

опять же всё субъективно. Если не понимать ограничений препроцессора, то можно и в самом простом ошибиться. (Я, например, для редких случаев сложных константных выражений применяю тесты на интерпретаторе Ch)

Цитата
Но, думается, сегодня уже нет проблем использовать плюсатый компилятор

К сожалению, есть.

Цитата
Некоторое время назад попался прикол (возможно байка

Аналогично
#define else ;
_Pasha
Пример 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);


Интересует субъективное мнение:
оправдано ли в данном случае применение макро?
Dog Pawlowa
Цитата(sensor_ua @ Apr 1 2008, 08:53) *
Простите, но что искать? По слову макрос? Я тут довольно часто, но мимо видно всё интересное по теме пробежало.


Я расскажу интересное! smile.gif Не далее как вчера час искал отсутствие фигурных скобок в макросе из двух команд, который использовался в if /else. На фоне какой-то непонятной потери синхронизации с JTAGом повеселился по полной программе. Сам себе клоун. sad.gif

Тем не менее сложные макросы использую, вариант с функциями inline не перекрывает всех возможных ситуаций. Использую один раз созданную конструкцию для организации доступа к переменным в протоколе обмена и переношу из проекта в проект, один раз отладив стиснув зубы smile.gif

Но создавать такое в каждом проекте - упаси Бог.

Для того, чтобы было понятно, о чем разговор, пример текста описания переменных.
В поддержку протокола в объект '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
Цитата(sensor_ua @ Apr 1 2008, 09:53) *
К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная.


Еще добавлю примерчик
Код
static PROGMEM char array[3] = {'A','B','C'};
................................
char a;
a = array[0];


Winavr сделает a='A' , поскольку array[0] рассматривается как константное выражение.
Сделают ли то же самое компилеры с услужливым сервисом "чтения флеша" - не уверен.
dxp
Цитата(sensor_ua @ Apr 1 2008, 12:53) *
А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятныеwink.gif варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации...

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

Цитата(sensor_ua @ Apr 1 2008, 12:53) *
К сожалению, это, 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'.

Цитата(sensor_ua @ Apr 1 2008, 12:53) *
К сожалению, есть.

И в чем они состоят? Для AVR хоть коммерческий IAR, хоть безплатный AVR-GCC давно уже поддерживают почти все фичи С++ (за исключением тяжелых, которые реально применять на AVR смысла нет).
sensor_ua
Цитата
Но это, повторяю, другое

Дальше бессмысленно обсуждать - лучше/хуже, видно/невидно и применять/не применять, рекомендуется/нерекомендуется суть разные вещи.
Цитата
никакой вменяемый компилятор этого делать не будет

для начала вменяемые компиляторы не будут ругаться на передачу в качестве фактического параметра функции const переменной, когда формальный параметр объявлен не const. В "прямоугольных" вариантах применения (таких как Вы показали) действительно интеллект компилятора может быть на высоте.
Цитата
Думается, что написать ключевое слово 'static' не является слишком обременительным.

Вы не поверитеwink.gif - использую описанные Вами приёмы и не стесняюсь
Цитата
И в чем они состоят?

Насчёт одинаково хорошо
http://www.klen.org/Projects/Embeded-gnu-t...last_build.html
http://www.klen.org/Projects/Embeded-gnu-t...gcc-cpp-how.txt
А насчёт нюансов использования, например, смотрим пост 7
http://electronix.ru/forum/index.php?showt...;p=247899&#
Не то, чтобы я против использования плюсов - этим нужно заниматься(С)
Но как раз реализации "плюсовости" у компиляторов гораздо более различны, нежели отклонения в работе их препроцессоров.
dxp
Цитата(sensor_ua @ Apr 1 2008, 15:17) *
Дальше бессмысленно обсуждать - лучше/хуже, видно/невидно и применять/не применять, рекомендуется/нерекомендуется суть разные вещи.

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

Цитата(sensor_ua @ Apr 1 2008, 15:17) *
для начала вменяемые компиляторы не будут ругаться на передачу в качестве фактического параметра функции const переменной, когда формальный параметр объявлен не const.


Вы про это:

Код
const int a = 10;
int b;

int g(int x, int y);

int main()
{
    g(a, b);
}


?

Если да, то вот результат:

Код
int main()
        main:
{
    g(a, b);
....               LDI     R30, LOW(b)
....               LDI     R31, (b) >> 8
8120               LD      R18, Z
8131               LDD     R19, Z+1
E00A               LDI     R16, 10
E010               LDI     R17, 0
........           CALL    ??g
}


Никакой ругани нет. И это не странно, если подумать и понять, что компилятор вполне адекватно оценивает ситуацию и не видит ничего опасного в таком вызове функции. Да и что тут может быть опасного? Ведь передача аргумента делается по значению. Вот если бы была попытка передать указатель (или ссылку) на константный объект, то это была бы ошибка, т.к. такая ситуация может привести к попытке изменить констатный объект внутри вызываемой функции. Но компилятор этого и не разрешает. А если какой-то компилятор разрешает, то это просто его баг и ничего более. Все эти правила стандартизованы и исходят из здравого смысла.

Что не так?

Цитата(sensor_ua @ Apr 1 2008, 15:17) *
В "прямоугольных" вариантах применения (таких как Вы показали) действительно интеллект компилятора может быть на высоте.

Хорошо, покажите свои "треугольные" или "круглые" варианты, где в подобной ситуации (с константами) компилятор облажается. Давайте пример в студию, посмотрим его. Интересно.

Цитата(sensor_ua @ Apr 1 2008, 15:17) *
Вы не поверитеwink.gif - использую описанные Вами приёмы и не стесняюсь

Тогда не понятно, какую точку зрения отстаиваете.

Цитата(sensor_ua @ Apr 1 2008, 15:17) *

И что конкретно там не так? Я никакого криминала не увидел. Особенно, в части константных объектов - там вообще ни слова не нашел про это. Может, плохо искал? smile.gif

Цитата(sensor_ua @ Apr 1 2008, 15:17) *
А насчёт нюансов использования, например, смотрим пост 7
http://electronix.ru/forum/index.php?showt...=247899�

И что? Манглинг имен - обычное дело в С++, жить реально не мешает. И опять совсем не понял, как это мешает использовать константные объекты вместо макросов?

Цитата(sensor_ua @ Apr 1 2008, 15:17) *
Но как раз реализации "плюсовости" у компиляторов гораздо более различны, нежели отклонения в работе их препроцессоров.

Реализации плюсовости у компиляторов отличаются по одной шкале - у одних поддерживаемых фич языка больше, у других - меньше. Но сами фичи если поддерживаются, то поведение дают одинаковое, т.к. есть на все это Стандарт. А вот на препроцессор стандарта нет, препроцессоров действительно существует некоторое количество, и совместимость у них скорее согласно традициям, нежели стандартам.
ReAl
Цитата(Lem @ Mar 31 2008, 22:08) *
как во что разворачивается макроопределение выяснить практически нереально (достоверно)

Как правило препроцессор идёт отдельным исполняемым файлом и/или у компилятора есть соответствующий ключик "выполнить только препроцессор".
И можно получить выход препроцессора и всё поразглядывать.



Да, очень много чего можно затолкать в enum-ы, inline-функции и т.п.
Да, в С++ есть ссылки, позволяющие уменьшить использование указателей и связанных с неправильным их применением ошибок (ударение на слове "неправильным", а не на слове "применением"). И т.д. и т.п.
Но, извините,
#define TRUE FALSE
и
#define SQR(x) x * x
это примеры вредительского и безграмотного применения макросов, а не их генетической ущербности.
Но на их базе строится рекомендация "использование макросов следует уменьшать в идеале до нуля", что, на мой взгляд, неправильно, как неправильны и рекомендации не использовать указатели (а почему тогда в стандарте оставили не только условную компиляцию, но и функциональные макросы, склейку и подстановку аргументов и оставили указатели?)

Так же как и с goto, который я использую редко и на форумах иногда сам привожу примеры, как можно обойтись без goto и при этом и сгенерированный код не растёт, и читаемость улучшается, но который я инода таки использую - так же и с макросами:
Используйте, не бездумно, голова дана именно для выбора нужного средства языка!
(Кстати, а какое средство языка можно использовать бездумно? Знаменитые примеры "индусского" кода написаны и без макросов, и без gotoи часто вообще на яве, изначально "лишённой недостатков С/С++")
Правильно и в нужном месте применённые макросы, на мой взгляд, только улучшают (не слишком хорошо формализуемый и очень вкусовой параметр) читаемость кода. Кончено, для людей, умеющих с ними обращаться (та же проблема с указателями).
Огрызок .h
Код
typedef WINAPI void (*DlPortWritePortUchar_t) (unsigned port, uint8_t d);
typedef WINAPI uint8_t (*DlPortReadPortUchar_t) (unsigned port);
typedef WINAPI void (*DlPortWritePortBufferUchar_t) (unsigned port, const uint8_t *pd, int len);

class lpt_dlportio_t : public lpt_io_t
{
public:
    lpt_dlportio_t(unsigned _base);
    ~lpt_dlportio_t();

    virtual uint8_t read(rd_offset offs);
    virtual void write(wr_offset offs, uint8_t d);
    virtual void write_data(const uint8_t *pd, int len);

private:
    HINSTANCE hdlportio;
#define _DL_PTR(_f_) _f_##_t _f_##_P
    _DL_PTR(DlPortWritePortUchar);
    _DL_PTR(DlPortReadPortUchar);
    _DL_PTR(DlPortWritePortBufferUchar);
#undef  _DL_PTR
}; // class lpt_dlportio_t


Огрызок .cpp
Код
lpt_dlportio_t::lpt_dlportio_t(unsigned _base)
    : lpt_io_t(_base), hdlportio(NULL)
{
    hdlportio = LoadLibrary("DLportIO.dll");
    if (hdlportio == NULL)
        throw error_win32_t(E_INVHARD, "Can't load port access library `DLportIO.dll'");

#define _DL_LOAD(_f_)\
        do {\
            _f_##_P = (_f_##_t)GetProcAddress(hdlportio, #_f_);\
            if(_f_##_P == NULL)\
                throw error_win32_t(E_INVHARD, "Can't load port access library `DLportIO.dll'"); \
        } while(0)

    _DL_LOAD(DlPortWritePortUchar);
    _DL_LOAD(DlPortReadPortUchar);
    _DL_LOAD(DlPortWritePortBufferUchar);
#undef _DL_LOAD

} // lpt_dlportio_t::lpt_dlportio_t


И не говорите мне, что этот текст надо переписать без макросов для облегчения чтения и сопровождения (представим себе, что понадобилось добавить ещё одну функцию из dlportio.dll и/или изменить обработку ошибки - в каком случае надо больше до-/пере-писывать руками и где при этом больше шансов ошибиться в одной из ветвей - в варианте с макросами или с прямым текстом?

И ещё пример на эту тему

http://www.telesys.ru/wwwboards/mcontrol/1...es/314290.shtml
SasaVitebsk
Очень интересная дискуссия развернулась. Но, в общем то, у меня действительно иногда хомуты с макросами происходят. Хотя, безусловно, это чисто мои ошибки в связи с неустоявшимися (для меня) правилами применения. (Как я считаю). На данном этапе не планирую отказываться от этого средства языка. Возможно потом пересмотрю данное решение.

В связи с этим вопрос. Как в IAR просмотреть результаты работы препроцессора. Например где можно увидеть результаты вычисления констант? В ряде случаев это реально помогло бы. smile.gif
Lem
одним из авторитетов для меня является Герб Саттер,ну, там ещё куча умных опытных людей, которые много поработали с БОЛЬШИМИ системами и знают, какова цена тех или иных ошибок при отладке и сопровождении проектов, создаваемых коллективами за длительные сроки и большие деньги
dxp
Цитата(SasaVitebsk @ Apr 1 2008, 19:48) *
На данном этапе не планирую отказываться от этого средства языка. Возможно потом пересмотрю данное решение.

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

Цитата(SasaVitebsk @ Apr 1 2008, 19:48) *
В связи с этим вопрос. Как в IAR просмотреть результаты работы препроцессора. Например где можно увидеть результаты вычисления констант? В ряде случаев это реально помогло бы. smile.gif

Да, такое средство очень кстати и меня не раз выручало. Ключик:

Код
--preprocess=[c][n][l] file|directory
                Preprocessor output
                   c     Include comments
                   n     Preprocess only
                   l     Include #line directives
Сергей Борщ
Цитата(SasaVitebsk @ Apr 1 2008, 15:48) *
Как в IAR просмотреть результаты работы препроцессора. Например где можно увидеть результаты вычисления констант? В ряде случаев это реально помогло бы. smile.gif
Project->Options->C/C++ compiler->Preprocessor-> галочка Preprocessor output to file. Только результатов вычисления там не будет - вычисляет компилятор в процессе оптимизации. Можно завести глобальную переменную и присвоить ей значение макроса, а дальше смотреть в листинге какое значение в эту переменную заносится.
sensor_ua
Цитата
Не следует путать приятное с полезным...

Не знаю о чём Вы, но я о том, что заявления, что что-то НЕ РЕКОМЕНДУЕТСЯ, стОит подкреплять не столько личным мнением/опытом (нисколько не умаляю Ваших достоинств), сколько какими либо ссылками на Стандарт.

Цитата
Тогда не понятно, какую точку зрения отстаиваете

Я считаю, что препроцессор - нужный и полезный инструмент. Да, огульное его применение не приветствуется, так как существует много описанных выше причин, по которым возникают ошибки. Я для супа использую ложку, а для бифштекса - нож и вилку. Я могу объяснить, почему вилкой есть суп не получится, но мне трудно объяснять ребёнку, почему нельзя есть бифштекс ложкой.
typedef и применение текстовых замен действительно очень похожи, и если нужен тип, то typedef предпочтительнее, чем текстовая подстановка, но ведь не просто так - они отличаются - свойства у typedef имеются, которых у текстовых подстановок нет. Препроцессор позволяет манипулировать с именами (склейка, подмена, удаление, индексация), что при отсутствии поддержки шаблонов на уровне самого языка является необходимым дополнением. Что касается сложных выражений, то применение препроцессора ограничено его ограниченными возможностями и он действительно не предупреждает о переполнениях и прочих глупостях. Но сложность бывает разная - где мы в уме считаем, где не прочь в столбик, а где и калькулятора не хватает. Потому рекомендации по отказу от применения препроцессора при любой возможности считаю необоснованными.
Цитата
Хорошо, покажите свои "треугольные" или "круглые" варианты, где в подобной ситуации (с константами) компилятор облажается. Давайте пример в студию, посмотрим его. Интересно.

Что касается константных объектов приведу пример, который говорит в пользу препроцессора как удобного инструмента
Это выдаёт ошибку в IAR
Код
typedef void (MY_FOO_TYPE)();
typedef struct {
  int a;
  int b;
  int c;
  MY_FOO_TYPE * foo;
} mytype;

void myfoo1(void);
void myfoo2(void);
#define sDefault_Value1 {   \
                       777, \
                         0, \
                        55, \
                    &myfoo1 \
}
#define sDefault_Value2 {   \
                       111, \
                       222, \
                      3333, \
                    &myfoo2 \
}


const int def1_aa = 777;
const int def1_bb = 0;
const int def1_cc = 55;
const MY_FOO_TYPE * def1_foo = &myfoo1;

mytype a = {0};

const mytype aaa = sDefault_Value1;
const mytype bbb = sDefault_Value2;
volatile int i;
int main(void){
  while(1){
    if(i == a.a){
      i = a.b;
      a = aaa; // ошибки нет
      a.a = def1_aa;
      a.b = def1_bb;
      a.c = def1_cc;
          a.foo = def1_foo;//!!!ошибка
//Error[Pe513]: a value of type "void () const *" cannot be assigned to an entity of type "void () *" D:\avrtst\avrtst\main.c 44

    }
    else{
      i = a.a;
      a = bbb;
    }

  }
  return 0;
}

void myfoo1(void){
;
}

Наверно правильно выдаёт

Цитата
А вот на препроцессор стандарта нет

п.6.10
http://upload.caxapa.ru/standards/ansi_c-i...c_9899-1999.pdf

Цитата
Да, такое средство очень кстати и меня не раз выручало. Ключик:

Отдельное спасибо
ReAl
Цитата(dxp @ Apr 1 2008, 15:11) *
Во всяком случае крайне желательно исключить повсеместное использование макросов препроцессора в качестве литералов и функций.
Ну в такой формулировке - с указанием где именно и без абсолютной категоричности - можно согласиться :-)
Но ведь обычно идёт разговор "запретить и всё тут, так как это источник ошибок".
Так человек, не разобравшийся в понятиях "побочный эффект" и "точка следования" рано или поздно накосячит с применением операторов ++, -- и ещё найдёт где. Но ведь никто не рекомендует отказаться от них и начать писать p = p + 1; Или уже рекомендуют? wink.gif
dxp
Цитата(ReAl @ Apr 1 2008, 19:40) *
Как правило препроцессор идёт отдельным исполняемым файлом и/или у компилятора есть соответствующий ключик "выполнить только препроцессор".

Это где такое правило? В GCC? Рад за него. В IAR с некоторых пор появился ключик. А вот в используемом мной сейчас VDSP такого удовольствия нет (не нашел). Т.ч. тут как повезет.

Цитата(ReAl @ Apr 1 2008, 19:40) *
Да, очень много чего можно затолкать в enum-ы, inline-функции и т.п.

Именно! smile.gif И все, что можно туда затолкать, не надо пихать в макросы. Об этом и спич.

Цитата(ReAl @ Apr 1 2008, 19:40) *
Но, извините,
#define TRUE FALSE

Да это я вообще в качестве хохмы привел, не апеллируя к этому примеру, как к аргументу. Просто этот пример наглядно демонстрирует мощь препроцессора как дестуктивного средства, только и всего.

Цитата(ReAl @ Apr 1 2008, 19:40) *
это примеры вредительского и безграмотного применения макросов, а не их генетической ущербности.

Как насчет вышеприведенного примера про

Код
#define I 7
#define N 2
#define C 0

?

А ведь это не лохи какие-то делали, а вполне серьезная и уважаемая фирма IAR Systems, которая выпускает очень достойные продукты. Замечательные грабли. Замечательный пример той же самой деструктивной мощи. Да, препроцессор обладает известной гибкостью в силу своих свойств по текстовой подстановке, но это палка о двух концах. Не стоит злоупотрелять ею.

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

Цитата(ReAl @ Apr 1 2008, 19:40) *
Но на их базе строится рекомендация "использование макросов следует уменьшать в идеале до нуля",

Да. Но идеал недостижим. Поэтому препроцессор останется.

Цитата(ReAl @ Apr 1 2008, 19:40) *
(а почему тогда в стандарте оставили не только условную компиляцию,

Потому, что для нее нет адекватной замены.

Цитата(ReAl @ Apr 1 2008, 19:40) *
но и функциональные макросы, склейку и подстановку аргументов

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

Цитата(ReAl @ Apr 1 2008, 19:40) *
и оставили указатели?)

Указатели из другой оперы, не надо все в кучу валить. Еще спроси (ехидно так), зачем оставили ассемблер - там тоже можно при невнимательном обращении наворотить. smile.gif


Цитата(ReAl @ Apr 1 2008, 19:40) *
Так же как и с goto, который я использую редко

Реально есть одна ситуация, где без него геморно - выход из вложенного цикла. Вроде все, больше не знаю (навскидку). Из-за этого оный оператор и оставили в языке.

Цитата(ReAl @ Apr 1 2008, 19:40) *
так же и с макросами:
Используйте, не бездумно, голова дана именно для выбора нужного средства языка!

Осталось найти, как добраться до головы иаровских разрабов, соорудивших макросы, приведенные выше. smile.gif Если есть возможность разложить грабли - они будут разложены. Раньше или позже, чаще или реже. Желательно, чтобы это было реже. Для этого использование препроцессора надо свести к минимуму, о чем только и речь. Никто не призывает отказаться от него вообще - это, повторяю, нереально. Чего в крайности кидаться.

Цитата(ReAl @ Apr 1 2008, 19:40) *
(Кстати, а какое средство языка можно использовать бездумно? Знаменитые примеры "индусского" кода написаны и без макросов, и без gotoи часто вообще на яве, изначально "лишённой недостатков С/С++")

Коренное отличие многих среств языка от средств препроцессора в том, что с ними код читается без подводных граблей. Сразу видно, как используется средство, сразу понятно, что тут можно ожидать. А вот с препроцессором оно несколько не так.

Цитата(ReAl @ Apr 1 2008, 19:40) *
И не говорите мне, что этот текст надо переписать без макросов для облегчения чтения и сопровождения (представим себе, что понадобилось добавить ещё одну функцию из dlportio.dll и/или изменить обработку ошибки - в каком случае надо больше до-/пере-писывать руками и где при этом больше шансов ошибиться в одной из ветвей - в варианте с макросами или с прямым текстом?

Это большой вопрос, как лучше, пример не убеждает. Насчет руками - про рефакторинг слышал? Кроме того, тут пример очень осторожного использования - определение макроса лежит прямо тут же, все перед глазами. Но не дай бог кто-нить вздумает использовать такое же имя _DL_PTR в своей программе. Будет удивлен. smile.gif Т.ч. тут тоже есть, что обсудить.

Цитата(ReAl @ Apr 1 2008, 20:40) *
Ну в такой формулировке - с указанием где именно и без абсолютной категоричности - можно согласиться :-)
Но ведь обычно идёт разговор "запретить и всё тут, так как это источник ошибок".

Это где это сказано, что "запретить" и все тут? Сказано было, свести к минимуму.
Сергей Борщ
Цитата(dxp @ Apr 1 2008, 16:50) *
Осталось найти, как добраться до головы иаровских разрабов, соорудивших макросы, приведенные выше. smile.gif
Да фиг с ними, с разработчиками. Ну достучишься, и "миллионы строк кода уже написанного, отлаженного и работающего кода сразу перестанут компилироваться" wink.gif
Код
#ifdef I
#undef I
#endif
#ifdef N
#undef N
#endif
#ifdef C
#undef C
#endif
Добавлять сразу после #include <ioavr.h>
Кстати, эти же грабельки перекочевали и в заголовочники avr-libc.
ReAl
Цитата(dxp @ Apr 1 2008, 15:50) *
Это где такое правило? В GCC? Рад за него.
Во-первых, "как правило" != "существует правило", во вторых, из правил есть исключения.
Необходимость выдавать ассемблерный листинг тоже нигде не прописана, но как правило эта возможность имеется.
На моей памяти большинство компиляторов позволяли просмотреть выдачу препроцессора.

Цитата(dxp @ Apr 1 2008, 15:50) *
Как насчет вышеприведенного примера про
Код
#define I 7
#define N 2
#define C 0

Ну что я скажу... У gcc в этом месте SREG_I, SREG_N и так далее.
Солидная фирма, не солидная - а бывает и на старушку прорушка.


Цитата(dxp @ Apr 1 2008, 15:50) *
Потому, что для нее нет адекватной замены.
Я спросил не "почему условную компиляцют оставили", а "почему вместе с ней оставили функциональные макросы, склейку, ..."

Цитата(dxp @ Apr 1 2008, 15:50) *
Совместимость и преемственность. Попробуй убери что-нить из этого - миллионы строк кода уже написанного, отлаженного и работающего кода сразу перестанут компилироваться.
Какого кода??? С++ ?
А какого лешего компилировать С-код С++-компилятором?
Или тогда решили, что всё, С-компиляторы больше никто не будет писать и даже развивать, и поэтому для сопровождения С-кода надо в С++ оставить явно вредные вещи? Могли бы в С++ режиме и устранить "огромный недостаток". Тем более, что в мелких, неочевидных и местами обидных случаях С++ таки сильно отличается от С (например, литерал 'я' в языке С имеет тип int а в С++ имеет тип char).


Цитата(dxp @ Apr 1 2008, 15:50) *
Указатели из другой оперы, не надо все в кучу валить. Еще спроси (ехидно так), зачем оставили ассемблер - там тоже можно при невнимательном обращении наворотить. smile.gif
А где ассемблер в стандарте языков С/С++ ??? laughing.gif


Цитата(dxp @ Apr 1 2008, 15:50) *
Для этого использование препроцессора надо свести к минимуму, о чем только и речь. Никто не призывает отказаться от него вообще - это, повторяю, нереально. Чего в крайности кидаться.
Коренное отличие многих среств языка от средств препроцессора в том, что с ними код читается без подводных граблей. Сразу видно, как используется средство, сразу понятно, что тут можно ожидать. А вот с препроцессором оно несколько не так.
Так в том-то и дело, что агитаторы за предельную минимизацию применения указателей а то иногда и тернарной операции говорят то же самое - "неочевидно, непонятно, в отличие от массивов, ссылок и if/else". И часто при этом столь же категоричны, как и в случае с goto - "не должно быть вообще", "по нашему стандарту предприятия goto запрещены". Так что кидаюсь в крайности не я.


Цитата(dxp @ Apr 1 2008, 15:50) *
Это большой вопрос, как лучше, пример не убеждает. Насчет руками - про рефакторинг слышал?
Это как - я добавляю в описание класса поле, а в конструкторе сразу по кнопочке "сделайте мне красвиво" сгенерируется для него
Код
нужное_имя = (нужный_тип)GetProcAddress(hdlportio, нужная_строка);
            if(нужное_имя == NULL)
                throw error_win32_t(E_INVHARD, "Can't load port access library `DLportIO.dll'");
Или всё же придётся ручками это делать?


Цитата(dxp @ Apr 1 2008, 15:50) *
Кроме того, тут пример очень осторожного использования - определение макроса лежит прямо тут же, все перед глазами. Но не дай бог кто-нить вздумает использовать такое же имя _DL_PTR в своей программе. Будет удивлен. smile.gif Т.ч. тут тоже есть, что обсудить.
Получит предупреждение о переопределении и сразу увидит что и где конфликтует.
Если код пишет так, что предупреждения валят десятками на каждый файл и поэтому отключил без разбору все предупреждения - то не помогут никакие ограничения - наксоячит.

Цитата(dxp @ Apr 1 2008, 15:50) *
Это где это сказано, что "запретить" и все тут? Сказано было, свести к минимуму.
Ну, ты не самый бескомпромиссный борец с препроцессором :-) И то считаешь, что "в идеале исключить вообще".
Я же считаю, что "в идеале" его может и неплохо бы заменить чем-то более грамотным, но оно почти наверняка будет более сложным и менее удобным (эти два фактора могут не уменьшить глюкодромистость, а увеличить), поэтому пусть лучше остаётся как есть и как с любым инструментом - его нужно знать, чтобы пользоваться правильно. А для этого нужно пользоваться. А чтобы было меньше отрезанных пальцев - не убирать с кухни ножи, а учиться ими пользоваться тогда, кода это надо и не махать ими, показывая, как летали листья. Но не говорить, что есть открывашки, консервные ножи и хлеб можно купить уже нарезанным, поэтому применение ножей
Цитата(dxp @ Mar 31 2008, 14:16) *
нужно свести к минимуму, в идеале исключить вообще. Но на практике это не удается сделать, поэтому приходится жить с ним. По большому счету ... имеет смысл использовать только
в затупленном виде для простеньких вещей типа намазывания масла.
Просто нужно учить тому, что каждый инструмент нужно использовать с умом. А не рекомендовать "минимизировать до отсутствия", так как пока научитесь - можете пораниться. А уж сколько людей ими убито... rolleyes.gif


Цитата(Сергей Борщ @ Apr 1 2008, 16:43) *
Кстати, эти же грабельки перекочевали и в заголовочники avr-libc.
Разве? Там ведь в common.h сидят SREG_*
Хотя, мне кажется, желание "облегчить переход с IAR" у них инода избыточное, могли и добавить односимвольные.
zltigo
Цитата(dxp @ Apr 1 2008, 16:50) *
А вот в используемом мной сейчас VDSP такого удовольствия нет (не нашел). Т.ч. тут как повезет.

Самостоятельные препроцессоры "C" не редкость, да и тем-же GCC прогнать можно... Посему на везение уповать совсем незачем smile.gif.


Цитата(ReAl @ Apr 1 2008, 17:51) *
А какого лешего компилировать С-код С++-компилятором?

А почему-бы и нет? Есть свои приятственные фичи в C++ компиляторах и для почти сишного кода. А как, например в смешанном коде без немерянных извращений работать с плюсовым кодом?
Лично я в большинстве случаев так и поступаю.


Цитата(ReAl @ Apr 1 2008, 16:40) *
Но ведь никто не рекомендует отказаться от них и начать писать p = p + 1; Или уже рекомендуют? wink.gif

Рекомендуют smile.gif С полгода назад на форуме проскаивала ссылка на переводную статью по "правильному стилю" именно с такой рекомендацией. У многих сей опус вызвал одобрение smile.gif. Я естественно, не мог смолчать - возражал...
SasaVitebsk
Цитата(zltigo @ Apr 2 2008, 00:44) *
Рекомендуют smile.gif

smile.gif А мне так понравилось. По сравнению с Паскалем, к примеру. smile.gif

А с другой стороны - сомневаешься, напиши так, чтобы сомнений не было. smile.gif

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

Тем не менее, с учётом данной дискуссии, сделаю выводы и обдумаю все ваши предложения. Попробую переосмыслить. А то действительно проблемы некоторые уже возникали. А всётаки было бы неплохо иметь элемент языка, который бы определял константы под которые гарантированно не выделяется память и которые вычисляются строго на этапе компиляции. Для IBM, экономия памяти не стоит как задача, а вот для мелкоAVRов былобы неплохо.
_Pasha
Цитата(SasaVitebsk @ Apr 2 2008, 01:17) *
По сравнению с Паскалем, к примеру. smile.gif


Анафема тому Паскалю, который не поддерживает функции inc(N,M), dec(N,M) smile.gif
А насчет пред/пост инкремента, например, действительно генератор граблей. Но пользоваться приятно... до тех пор, пока есть уверенность в адекватной трансляции.
defunct
Цитата(ReAl @ Apr 1 2008, 15:40) *
Но ведь никто не рекомендует отказаться от них и начать писать p = p + 1; Или уже рекомендуют? wink.gif

Delphi'сты рекомендуют smile.gif

Цитата(dxp @ Apr 1 2008, 15:50) *
Как насчет вышеприведенного примера про
Код
#define I 7
#define N 2
#define C 0

?

Я еще люблю применять
#define K 1024
#define M K*K

Просто кому придет в голову объявить переменную ОДНОЙ ЗАГЛАВНОЙ буквой?

Цитата(SasaVitebsk @ Apr 2 2008, 00:17) *
smile.gif А мне так понравилось. По сравнению с Паскалем, к примеру. smile.gif

++ / -- - просто создан для индексации массивов.
тоже неплохо смотрится в условиях.

а повсеместно тыкать ++ / -- ни ни, я отдаю предпочтение p += 1 вместо ++;
andrew_b
Цитата(defunct @ Apr 2 2008, 12:58) *
Delphi'сты рекомендуют
Они сишникам не указ, и даже не авторитет.
ReAl
Цитата(zltigo @ Apr 1 2008, 22:44) *
Самостоятельные препроцессоры "C" не редкость, да и тем-же GCC прогнать можно... Посему на везение уповать совсем незачем smile.gif.
Тут можно спорить о возможном несовпадении (один из них или оба могут не польностью соответствовать стандрату и проверка одним другого будет неадекватной). Но в целом он чаще есть, чем его нет. Я их даже эпизодически прикручивал для своих нужд (например, в pldasm какие-то кусочки пихал для облегчения написания).

Цитата(zltigo @ Apr 1 2008, 22:44) *
А почему-бы и нет? Есть свои приятственные фичи в C++ компиляторах и для почти сишного кода. А как, например в смешанном коде без немерянных извращений работать с плюсовым кодом?
Лично я в большинстве случаев так и поступаю.

Это если говорить о сложившейся ситуации. И я тоже так делаю иногда, хотя и inline-функции, и объявление переменных по месту, включая счётчики в циклах for - есть в C99, просто до сих пор не все поддерживают. Да и в С99 есть не менее вкусная возможность инициализировать элементы структур/массивов по имени, в С++ этого нет. Так что... Где найдёшь, где потеряешь...

А если говорить на момент "до того", когда язык С++ только сбивался и ещё и люди не привыкли пользоваться "С-совместимыми" расширениями С++, и "массы текстов" не набралось, то кто мешал "таки повыкидывать из С++ тяжкое наследие С, не приносящее ничего, кроме вреда"?
По поводу смешанных проектов - никто не мешает весь С-текст держать в отдельных файлах, компилировать в С-режиме со всеми функциональными макросами, а потом линковать с С++ - программой, скомпилированной в С++ режиме.
Ввели бы для условной компиляции что-то как-бы похожее, но интегрированное в язык, а не являющееся отдельным проходом. Как результат в условии компиляции можно было бы применять и enum-определённые константы и т.п. Кто мешал? И не было бы споров smile.gif Ну скучал бы я по старым добрым макросам smile.gif


Цитата(_Pasha @ Apr 2 2008, 08:58) *
А насчет пред/пост инкремента, например, действительно генератор граблей.
Где? Как?
Может, генератором граблей есть голова пишущего, не разобравшаяся в побочных эффектах и точках следования?
Если так, то надо требовать изменения консти стандарта С таким образом, чтобы все переменные по умолчанию были volatile, так как от непонимания этого момента "глюков" ещё больше.
Ну или просто компилировать свои программы с выключенной оптимизацией.


Цитата(defunct @ Apr 2 2008, 11:58) *
а повсеместно тыкать ++ / -- ни ни, я отдаю предпочтение p += 1 вместо ++;
lol.gif
Шаманим потихоньку?

Стандарт С:
Цитата
6.5.3.1 Prefix increment and decrement operators
...
Semantics
2 The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation. The expression ++E is equivalent to (E+=1). See the discussions of additive operators and compound assignment for information on constraints, types, side effects, and conversions and the effects of operations on pointers.
3 The prefix -- operator is analogous to the prefix ++ operator, except that the value of the operand is decremented.

Цитата
6.5.16.2 Compound assignment
Semantics
3 A compound assignment of the form E1 op= E2 differs from the simple assignment expression E1 = E1 op (E2) only in that the lvalue E1 is evaluated only once.


++E эквивалентно E+=1 всегда
любое из них эквивалентно E = E + 1 в подавляющем большинстве случаев.
Разница будет, например, для a[i] += 1; в случае, когда i объявлено как volatile, иначе выражения семантически эквивалентны.
defunct
Цитата(ReAl @ Apr 2 2008, 13:55) *
lol.gif
Шаманим потихоньку?
++E эквивалентно E+=1 всегда

Это я в курсе smile.gif
Дело принципа и привычки, от ++ в глазах рябит. Как и написал выше ++/-- использую только для индексации и в условиях:

while( zz--)
{
*p++ = xx;
p[ idx++] = xx;
}
...

а когда стоит вопрос одинокого инкремента какой-то переменной или указателя то пользую

p += 1;
Сергей Борщ
Цитата(defunct @ Apr 2 2008, 12:58) *
Просто кому придет в голову объявить переменную ОДНОЙ ЗАГЛАВНОЙ буквой?
Ну как же? локальную переменную типа char для цикла в puts так и хочется обозвать заглавной C.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.