Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 08:45:00

Коллеги!
Можете закидать меня объедками, но не могу молчать! Хочу поделиться опытом, который, с моей точки зрения, может быть полезен всем, но по странным необъяснимым причинам как-то не известен широко.

Речь пойдет о GCC - бесплатном компиляторе для разных платформ. Для "больших" систем, наверное, мои советы будут лишними, а вот для AVR, мне кажется, очень хороши. Так же менее интересен этот опыт тем, кто любит и активно использует С++, я же, как истинный ретроград, применяю Си, и хочу поделиться тем, как можно (и нужно!) сделать работы с проектами на этом языке более комфортными.

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

2. Секции памяти.
В GCC есть несколько типов секций памяти, задаваемых при помощи атрибута section("название_секции"). Особенно полезными, с моей точки зрения, следующие (указываю конкретное название_секции):
.noinit - все переменные, помещенные в эту секцию, не будут инициализированы "по умолчанию". Как известно, все статические (и глобальные) переменные в Си автоматически инициализируются нулями, есл не указано конкретное значение. Но порой бывает необходимо, чтобы после сброса (например, по WDT) переменная сохранила свое прежнее, досбросовое значение, и эта секция позволяет получить требуемое. Если поместить в этой секции массив, то посчитав CRC этого массива после сброса, можно получить весьма неплохое СЛУЧАЙНОЕ число, по-настоящему случайное, а не псевдослучайное. Чем больше массив, тем лучше случайность. Надеюсь, вы сами понимаете, почему.

.initX, где Х - номер секции от 0 до 9. Это секция для кода. Чтобы было понятно: сразу после сброса выполняется код, помещенный в секцию .init0, потом постепенно номер секции увеличивается, и функция main выполняется в секции 10. То есть пометив атрибутом с указанием определенной секции свою функцию, вы заставите ее выполниться ДО НАЧАЛА main. Только будьте осторожны: параметры стека задаются в секции 2, а до этого стек еще не определен. Я рекомендую использовать секции поближе к main, т.е. 6...9.

Зачем это надо? О! Собственно, ради этого я и затеял эту писанину! Чуть позже расскажу.

.finiX - это "зеркально" обратные секции к только что описанным: в порядке уменьшения своего номера они выполняются ПОСЛЕ ВЫХОДА ИЗ main, то есть сначала код из секции .fini9, потом меньше и меньше, и в 0-й реализуется запрет прерываний и m1: rjmp m1. Почему-то принято считать, что в МК жизни за пределами main нет, и тем более ее нет ПОСЛЕ main, но это, как видите, не так! И, хотя применений этим секциям можно найти меньше, чем инициализирующим, все-таки можно извлечь пользу и из них.

3. Потоковый ввод-вывод.
Об этом я уже писал статью, если интересуетесь - почитайте: WinAVR: консольный ввод-вывод Если кратко, то смысл в том, чтобы заставить работать функции printf и scanf так, как это и было задумано в Си - для вывода и ввода. Как-то прижился подход, когда вместо этих функций используют sprintf и sscanf, т.е. работают со строками, а вот получение этих строк приходится "длать ручками". Если же реализовать потоковый ввод-вывод, а это совсем не сложно! - то можно избавиться и от этого. Поверьте, когда вы работаете с консольным выводом (например, через USART), это просто кардинально упрощает жизнь! Кстати, совсем не лишней будет эта возможность при работе с файлами на SD-карте, что тоже многие применяют в своих проектах.

Ну, а теперь о том, какая польза может быть извлечена из вышеописанных секций.
Буду вести рассказ про себя, а мой опыт каждый может попробовать примерить на себя, чтобы решить, подходит ли, полезен ли он, или нет.

Я часто делаю проекты из кучи модулей. И многие из них работают с периферией. И периферию эту надо настраивать.
Как принято делать? в каждом таком модуле создают функцию init_xxx(), которую затем вызывают из main.
Как делаю я? эту самую функцию init_xxx() я помещаю в секцию .init7 и она вызывается сама! А в main о периферии я вообще не забочусь - все уже подготовлено само!

Какие плюсы я получаю?
Во-первых, код main становится лаконичным и красивым, в нем именно то, что и должно быть: ГЛАВНАЯ работа. Надо лампочками мигать - они мигают, надо файлы читать - они читаются... а вякая ерунда вроде портов и регистров скрыта в модулях.

Как я это делаю?
Кто скачивал мои исходники, наверняка видел там файл avr_helper.h - в нем я определил ряд макросов, при помощи которых все это и делается. Вот самые интересные из них:

Код:
// вспомогательный макрос конкатенации макросов
#define _CONCAT_(x,y)      x ## y
/// макрос конкатенации основной
#define CONCAT(y,x)      _CONCAT_(y,x)

#define DDR(x)            CONCAT(DDR,x)
#define PORT(x)            CONCAT(PORT,x)
#define PIN(x)            CONCAT(PIN,x)

/// макрос для определения функции-инициализатора (вызывается автоматически в секции .inix)
#define INIT(x)         static void __attribute__((naked, used, section(".init" #x))) CONCAT(_init_, __COUNTER__) (void)
/// то же самое для завершающих функций
#define DONE(x)         static void __attribute__((naked, used, section(".fini" #x))) CONCAT(_fini_, __COUNTER__) (void)
/// это если лень писать - самая ранняя инициализация
#define AUTOINIT()      INIT(2)
/// макрос для определения компактной версии функции main()
#define MAIN()         int __attribute__((OS_main)) main(void)
/// неинициализируемые переменные
#define NOINIT         __attribute__((section(".noinit")))


Как видите, я использую макросы для секций памяти, ибо писать длинные строки муторно. Но - обратите внимание! - как я делаю функции инициализации-завершения: я использую встроенные макросы GCC для генерации уникального имени функции, чтобы при необходимости в листинге их найти можно было. __COUNTER__ это макрос, который автоматически увеличивается на 1 всякий раз, когда используется. Т.е. я могу написать в коде модуля несколько INIT(7) в итоге сгенерируется несколько разноименных функций, все из которых попадут в одну секцию.

Теперь в модуле, который работает с портами ввода вывода, я просто пишу нечто подобное:
Код:
INIT(7){
   DDR(PORT_IO) |= _BV(PIN_IO);
}
и на этом мои переживания по поводу инициализации периферии заканчиваются. Да, конечно, в заголовочном файле модуля я определяю следующие макросы
Код:
#define PORT_IO    D
#define PIN_IO   2

Обратите внимание: для задания порта ввода-вывода я использую не привычное PORTD, а только букву D. Знаете зачем? Чтобы так же просто и быстро перенастраивать ВСЕ СВЯЗАННЫЕ с этой буквой регистры при помощи макросов DDR(PORT_IO), PORT(PORT_IO) и PIN(PORT_IO). По-моему, это очень удобно.

Кстати, в INIT-секциях удобно считывать их EEPROM значения конфигурационных параметров устройства. А вот в секциях DONE иной раз удобно их сохранять. Правда, для этого надо обеспечить 2 условия:
1. обязательное завершение main, т.е. выход из основного цикла
2. тот самый выход должен быть связан с нажатием кнопки "выключения". Т.е. как минимум в проекте должна быть такая кнопка.
Эти условия ограничивают применимость завершающих секций, но если вы делаете проект, управление питанием в котором реализуется через кнопку, заведенную на МК, это так же поможет вам сделать красивый код сохранения настроек, сведя при этом количество записей EEPROM к минимуму.

Знатоки С++ наверняка узнали в секциях .init-.fini аналоги конструкторов и деструкторов... :))) Кстати, именно там GCC и размещает конструкторы-деструкторы статических экземпляров классов.

Ну еще пара слов о моих подходах.
Главное: при инициализации портов и регистров нужно всегда использовать битовые операции ИЛИ и И для установки битов в регистрах периферии, применение прямой записи крайне нежелательно по одной причине: вы не знаете заранее, какой инициализирующий код из нескольких будет выполнен первым, а какой следующим. Но вы знаете, что после сброса все регистры обнулены (точнее - имеют дефолтное значение). Поэтому в инициализации каждого модуля вы обязаны изменять только те биты в регистрах, которые требуются этому модулю, сохраняя остальные неизменными.
Это, конечно, слегка раздувает код "лишними" операциями над всякими DDRx и т.п., но это небольшое зло, как мне кажется, с лихвой компенсируется удобством программирования.

И еще одно. Задавая разные номера для макроса INIT, вы можете управлять порядком вызова инициализации. Разумеется, что код в INIT(7) будет выполнен раньше, чем INIT(8). Просто помнить номера инициализаций в нескольких модулях сложновато. Я завел себе правило: если мне надо просто проинициализировать модуль, я использую секцию 7. Если надо гарантировать превентивность инициализации - 6. Если надо гарантировать "запоздалость" - 9. Иных вариантов стараюсь избегать.

Вот и все.

Буду рад, если кому-то открыл глаза или помог стать лучшим программистом, чем ранее. :)))

Если хотите, у могу еще рассказать о том, как можно делать трассировку кода удобно, но накладно по ресурсам... Это полезно, если МК большой, а аппаратного отладчика нет...

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 09:32:30

Мне интересно, продолжайте, пожалуйста.

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 11:15:53

ARV писал(а):Я часто делаю проекты из кучи модулей. И многие из них работают с периферией. И периферию эту надо настраивать.
Как принято делать? в каждом таком модуле создают функцию init_xxx(), которую затем вызывают из main.
Как делаю я? эту самую функцию init_xxx() я помещаю в секцию .init7 и она вызывается сама! А в main о периферии я вообще не забочусь - все уже подготовлено само!

Какие плюсы я получаю?
Во-первых, код main становится лаконичным и красивым, в нем именно то, что и должно быть: ГЛАВНАЯ работа. Надо лампочками мигать - они мигают, надо файлы читать - они читаются... а вякая ерунда вроде портов и регистров скрыта в модулях.

И еще одно. Задавая разные номера для макроса INIT, вы можете управлять порядком вызова инициализации. Разумеется, что код в INIT(7) будет выполнен раньше, чем INIT(8). Просто помнить номера инициализаций в нескольких модулях сложновато. Я завел себе правило: если мне надо просто проинициализировать модуль, я использую секцию 7. Если надо гарантировать превентивность инициализации - 6. Если надо гарантировать "запоздалость" - 9. Иных вариантов стараюсь избегать.

Я правильно понимаю, что вместо того чтобы сделать как-то так:
Код:
void init()
{
    init_xxx();
    init_yyy();
    init_zzz(9600);
}

void main()
{
    init();
}

Ты делаешь так:
Код:
// module xxx
INIT(6) {
    init_xxx();
}

// module yyy
INIT(7) {
    init_yyy();
}

// module zzz
INIT(9) {
    init_zzz(9600);
}

void main()
{
}

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 12:35:41

да, именно так и выходит. более того, в модулях вместо
Код:
void init_xxx(void){
// some code
}

INIT(7){
   init_xxx();
}
я сразу делаю
Код:
INIT(7){
   // some code
}


Добавлено after 40 minutes 53 seconds:
Rtmip писал(а):Мне интересно, продолжайте, пожалуйста
Ну, раз интересно, вот еще несколько идей, как и обещал, про трассировку.

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

Вот что я сделал.
Сначала я организовал с помощью вышеописанных возможностей модуль с названием debug.c, при подключении к проекту которого сразу инициализируется файловый вывод в USART на подходящей скорости (обычно я использую 57600 бод). В этом заголовочнике debug.h этого модуля все функции определены при помощи директив условной компиляции
Код:
#if defined(__DEBUG__)
   void debuf_func(void);
#else
#define debug_func()
#endif
То есть если проект собирается в релизной версии (макрос __DEBUG__ не определен), то все функции заменяются заглушками-пустышками, что гарантирует их исключение из прошивки, т.к. оптимизатор выбросит неиспользуемые функции из кода (если вы еще не знаете, как это сделать - скажу и об этом, хотя это должен знать каждый GCC-шник). Определить директиву __DEBUG__ можно при помощи параметра командной строки -D компилятора, т.е. так: -D__DEBUG__, при этом при компиляции любого файла этот макрос будет "виден".

Затем я определил ряд макросов для трассировки одной или нескольких переменных. Привожу пример для одной переменной типа int, по аналогии можно сделать, сколько надо
Код:
#define log_i(x)   printf_P(PSTR("\nLOG> " __FILE__ "\%s:%04d " # x "=%d\n"), __FUNCTION__, __LINE__, x)
Теперь если этот макрос вставить в своем файле my_file.c в любом месте, например так (показываю номера строк):
Код:
013: void func(void){
014:    int test_var = 12;
015:    log_i(test_var);
016:    // какой-то код
017:}
то после компиляции и прошивки в микроконтроллер при входе в эту функцию в USART будет выведена следующая строка:

LOG> ..\my_file.c\func:0015 test_var=12

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

Какие возможности при этом использованы?
1. Предопределенный макрос с именем файла __FILE__. Этот макрос содержит "полный путь" к файлу исходного текста модуля, как его видит компилятор, т.е. если там иерархия подпапок, они тоже будут в этом макросе
2. Конкатенация строковых констант. Запись "\nLOG> " __FILE__ слепит из двух строк одну, в моем примере получится такая "\nLOG> ..\my_file.c"
3. Предопределенный макрос __FUNCTION__, содержащий указатель на строку символов с именем функции. Обратите внимание, что это не константа типа строка, а переменная, потому для ее вывода надо указать формат %s
4. Предопределенный макрос __LINE__, равный номеру строки исходного текста, т.к. это число, для его вывода нужен формат %04d
5. Действие директивы # препроцессора, которая превращает один идентификатор правее себя в его строковый эквивалент. То есть передавая в макрос идентификатор переменной мы получим строковую константу с этим идентификатором.

Вот так, просто и незатейливо, можно организовать трассировку всего, чего угодно. Разумеется, все эти фичи можно использовать и для других целей.

Но за это придется заплатить:
- повышенным расходом flash - prinft отъест у вас примерно 1,5 килобайта, плюс все "текстовые" сообщения с именами файлов, функций, номерами строк и т.п., которые благодаря PSTR тоже попадут во flash
- повышенным расходом ОЗУ

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

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 12:45:24

ARV писал(а):да, именно так и выходит.

Так а преимущества то в чем? Хотел разгрузить main, я тебе привел пример где в main одна строка. Но дополнительно я в одном месте вижу какие функции инициализации вызываются, в каком порядке, причем этот порядок легко менять, и могу передавать в них параметры.

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 12:53:22

Все преимущества я описал.
Например, вы сделали модуль для работы с выводом "в консоль", т.е. USART. Какие параметры вы собрались передавать в функцию инициализации? Скорость? И собираетесь ее менять по ходу исполнения программы? Не лучше ли задать константу скорости в заголовочнике того модуля, и забыть вообще про него? Зачем вам нужен порядок инициализации, если к моменту начала main уже все инициализирвоано - хоть USART, хоть ЖКИ, хоть черт в ступе?

У меня вот есть давно сделанный модуль с названием usart_io.c с заголовочником usart_io.h. В заголовочнике прописана скорость 57600, и за последние 4 года я ни разу ее не менял.
если мне надо выводить в консоль, я просто кидаю эти файлы в папку проекта, и где захочется, пишу printf - все, усилия кончились на этом. Разве что попадется МК, в котором более 1 USART - тогда приходится имена регистров подправлять, да и то из-за моей же лени - ведь можно при помощи условной компиляции предусмотреть и такой вариант...

аналогично я поступил и с ЖКИ.

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 15:40:57

ARV писал(а):Все преимущества я описал.
Например, вы сделали модуль для работы с выводом "в консоль", т.е. USART. Какие параметры вы собрались передавать в функцию инициализации? Скорость? И собираетесь ее менять по ходу исполнения программы? Не лучше ли задать константу скорости в заголовочнике того модуля, и забыть вообще про него? Зачем вам нужен порядок инициализации, если к моменту начала main уже все инициализирвоано - хоть USART, хоть ЖКИ, хоть черт в ступе?

У меня вот есть давно сделанный модуль с названием usart_io.c с заголовочником usart_io.h. В заголовочнике прописана скорость 57600, и за последние 4 года я ни разу ее не менял.
если мне надо выводить в консоль, я просто кидаю эти файлы в папку проекта, и где захочется, пишу printf - все, усилия кончились на этом. Разве что попадется МК, в котором более 1 USART - тогда приходится имена регистров подправлять, да и то из-за моей же лени - ведь можно при помощи условной компиляции предусмотреть и такой вариант...

Во-первых, порядок инициализации может иметь значение, наверно именно потому у тебя и можно менять номера секций чтобы гарантировать превентивность или "запоздалость"... Правда там даже чтобы понять в каком порядке происходит инициализация нужно взять ручку и пройтись по всем модулям :) Во-вторых, мой модуль USARTа принимал бы параметр скорости как минимум. По факту, т.к. я пишу на C++, он принимает намного больше, фактически все что нужно чтобы можно было обойтись без дополнительных определений в других файлах. Скорость USART тоже приходится менять, иногда периодически по ходу работы(DS18B20), бывает изначально даже не известно какая она будет. В идеале любой модуль должен переноситься между проектами без изменений, в чистом С этого добиться сложнее, но по крайней мере можно к этому стремиться.

Добавлено after 6 minutes 57 seconds:
ARV писал(а):Вот так, просто и незатейливо, можно организовать трассировку всего, чего угодно. Разумеется, все эти фичи можно использовать и для других целей.

Это как бы повсеместно распространенный подход, обычно такой код пихают в assert. На AVR наверно так реже делают, место экономят :)

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 19:05:06

Reflector писал(а):Во-первых, порядок инициализации может иметь значение, наверно именно потому у тебя и можно менять номера секций чтобы гарантировать превентивность или "запоздалость"...
во-первых, номера секций менять можно не у меня, а у GCC :))) я лишь не лишаю никого этой возможности своими макросами.
во-вторых, инициализация и управление по ходу пьесы - это разные вещи. инициализация задает состояние по умолчанию, для которого не требуются параметры. если хочется, по умолчанию можно настроить сразу так, чтобы работало, как хочется. а потом, если хочется, можно перенастраивать.
в-третьих, если вы пишите на С++, это еще не доказательство, что вы выбрали самый лучший подход.
кстати, у вас в С++ конструкторы статических экземпляров классов GCC сам помещает в секцию .init6 по-моему (могу ошибаться, т.к. не помню наизусть) - это вас не смущает? у меня то же самое, но для Си (если считать модуль эквивалентом экземпляра класса)

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

Добавлено after 3 minutes 29 seconds:
Reflector писал(а):В идеале любой модуль должен переноситься между проектами без изменений, в чистом С этого добиться сложнее, но по крайней мере можно к этому стремиться
переносимость модуля не от Си зависит, а от связи модуля с аппаратной частью. для AVR редко когда можно добиться полной абстракции модуля от аппаратуры, потому и переносимость модулей между проектами не высокая.

Re: Использование особенностей GCC для повышения комфорта

Сб апр 22, 2017 21:56:20

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

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

в-третьих, если вы пишите на С++, это еще не доказательство, что вы выбрали самый лучший подход.
кстати, у вас в С++ конструкторы статических экземпляров классов GCC сам помещает в секцию .init6 по-моему (могу ошибаться, т.к. не помню наизусть) - это вас не смущает? у меня то же самое, но для Си (если считать модуль эквивалентом экземпляра класса)

Я пишу для STM32, на C++, классы в большинстве своем полностью статические и шаблонные, у них всегда есть пустой конструктор, метод init и конструктор вызывающий этот init. Допустим было бы как ты хочешь, создаем глобальный экземпляр класса USART, он помещает вызов конструктора в секцию .init6, тот вызывает setBaudRate в который передается 57600. А знаешь что внутри setBaudRate? Там считывается текущая скорость одной из APB шин, на которых и висят USARTы, чтобы правильно проинитить регистр BRR. И какая на тот момент скорость APB? Ты пишешь для AVR, так что я распишу подробнее... Чтобы получить правильную частоту к этому моменту нужно определится с тем используется ли встроенный генератор или кварц, нужно ли PLL, если нужно, то дополнительно задается 3 делителя и наконец есть еще делители для каждой шины, в том числе для нужной нам APB, их тоже нужно задать. В итоге придется в модуль настройки тактирования добавить еще десяток дефайнов и настраивать все через них, причем тут важен порядок, это все должно вызываться до других модулей зависимых от скорости генератора... Естественно я так не делаю и вызываю инициализацию усарта когда уже установлена рабочая частота генератора, перед которой была задана нужная латентность флеша зависимая от этой частоты, опять же не через дефайны в модуле настройки латентности флеша или где они там должны в этом случае находиться...

Reflector писал(а):переносимость модуля не от Си зависит, а от связи модуля с аппаратной частью. для AVR редко когда можно добиться полной абстракции модуля от аппаратуры, потому и переносимость модулей между проектами не высокая.

Смотри, берем какой-нибудь бюджетный STM32 за 2$, у которого 5 USARTов и дофига флеша. На C++ я могу проинициализировать все пять USART пятью строками, причем в качестве параметра даже пара пинов передается, т.к. они могут быть разными, или размер буферов, если используется буферизация. Правда в последнем случае придется вызвать еще одну функцию на каждый USART в обработчике соответствующего прерывания. Как это можно нормально разрулить на С, да еще и с применением твоей методики я даже не представляю :)

Re: Использование особенностей GCC для повышения комфорта

Вс апр 23, 2017 10:57:12

Reflector писал(а): Как это можно нормально разрулить на С, да еще и с применением твоей методики я даже не представляю
вам крупно не повезло, вам придется не применять мою методику. чего вы так всполошились - ума не приложу. :dont_know:

кстати, вроде как для вас я писал:
ARV писал(а):менее интересен этот опыт тем, кто любит и активно использует С++

Re: Использование особенностей GCC для повышения комфорта

Вс апр 23, 2017 11:14:17

ARV писал(а):кстати, вроде как для вас я писал:
ARV писал(а):менее интересен этот опыт тем, кто любит и активно использует С++

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

Re: Использование особенностей GCC для повышения комфорта

Вс апр 23, 2017 12:31:51

Reflector писал(а):применительно к мк такой подход плох

просто вам не повезло. я от такого подхода кайфую :))) и по мне он просто великолепен.

Re: Использование особенностей GCC для повышения комфорта

Сб июн 03, 2017 10:24:41

Интересно, интересно. :) Когда-то я ради прикола передавал аргументы в main(), используя похожие методы. :)

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

Re: Использование особенностей GCC для повышения комфорта

Пн июн 05, 2017 12:54:05

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

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

Re: Использование особенностей GCC для повышения комфорта

Пн июн 19, 2017 18:59:03

у такого повышения комфорта есть некоторая проблема - "особенности компилятора" зачастую существуют только в нем. Тот же IAR, к примеру, не обязан быть в курсе (и он таки не), что там за __attribute__ такие. Можно, конечно, сказать "не, я всегда использовал и буду использовать только один компилятор", но сие в общем не есть хороший подход.

Re: Использование особенностей GCC для повышения комфорта

Вт июн 20, 2017 07:34:38

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

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

то есть фактически нет возможности сделать исходник под любой компилятор и платформу. так к чему самообманываься? ну не собирается IAR-ом - да и попутного ему ветра! GCC бесплатно скачивается и за 5 минут устанавливается, после чего все можно собрать и удалить GCC снова, если уж так приспичит.

Re: Использование особенностей GCC для повышения комфорта

Вт июн 20, 2017 10:40:07

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

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

что-то мне подсказывает, что вы их мало видели :dont_know: Большинство опенсорца обычно без проблем собирается на разнообразных *nix-овых платформах GCC, clang-ом и интеловским компилятором; на винде - MSVC, тут да, иногда с бубном.

Re: Использование особенностей GCC для повышения комфорта

Вт июн 20, 2017 10:43:08

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

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

Что же касается продвинутого использования препроцессора, я как раз не вижу в этом ничего особенно плохого для "боевого" кода. В этом случае и получается истинная кросс-платформенность: перенес исходники, написал в единственном заголовочнике что-то в духе "#define IAR_MODE" (а можно и не писать, многие компиляторы сами определяют уникальные для себя символы) - и все, модуль компилируется. При этом в код можно совсем не заглядывать.

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

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

Re: Использование особенностей GCC для повышения комфорта

Вт июн 20, 2017 11:09:46

При всем уважении, YS, останусь при своем мнении.
Можно пытаться решить возникающие проблемы, а можно не создавать их. И второй вариант мне ближе. Берется бесплатный компилятор и принимается решение, что никаких вариантов более быть не может. С этого момента у меня нет никаких проблем, даже с совестью по поводу ломаного IARа.

Ну а все желающие могут продолжать бороться с придуманными проблемами - кто ж запретит? Мы же не на производстве, где кто-то установил правила и требует их соблюдать! Мы - любители, мы правила сами для себя придумываем. Я для себя придумал и поделился. :) и всем советую проблем себе не придумывать.

Re: Использование особенностей GCC для повышения комфорта

Вт июн 20, 2017 12:07:42

Господи спаси, я не настаиваю на том, чтобы кого-то переубедить.

Просто для меня это имеет чисто практическое значение. У нас в лаборатории, например, живет зоопарк компиляторов по причине разнообразия используемых компонентов. Так что хочется или нет, но о кросс-платформенности думать приходится, чтобы при необходимости использовать код коллег и делиться с ними своим кодом.

Еще я люблю отлаживать аппаратно-независимые модули на x86, а потом переносить в железо. Вот, например, в текущем проекте мне пришлось писать парсер текстовых команд. Разумеется, отлаживать такое проще на ПК, с обширным тестовым выводом. Кстати, такая практика приучила меня всегда указывать тип констант явно, потому что невинно выглядящий сдвиг типа 1<<8 на x86, x86-64, ARM, MSP430, AVR и STM8 может вести себя по разному. А вот 1UL<<8 работает гораздо более предсказуемо. :)

Ну и GCC хорош, но не везде. Например, для него так и не сделали кодогенератор для STM8, что меня очень огорчает. Приходится использовать Cosmic, потому что желание использовать SDCC у меня отпало когда я дочитал документацию по нему примерно до половины. Ну а IAR стоит безумных денег, и лицензионная политика у него просто параноидальная.
Ответить