Перейти к содержанию

inline или макрос в многофайловых проектах?


Рекомендуемые сообщения

Есть у нас функция инициализации какого-то модуля или устройства, либо функция, которая просто возвращает значение регистра. Логичнее эти функции писать в файлах "драйверах", а не в main. Ну хотя бы с точки зрения переносимости кода между проектами. Но сразу появляется вопрос - что удобно программисту, то неудобно компилятору. Смысла в вызове однократно используемой функции или функции с одной командой нет - такие функции разумнее встроить в код. Но вот как быть - inline из другого *.c файла не вызывается, то бишь такую функцию надо писать в заголовке. Но такую функцию можно написать и как макрос (в том же заголовке - макрос в *.c файле тоже не виден али нет?). Так вот меня разрывает внутренняя устойчивая связь - как же поступить?

Ссылка на комментарий
Поделиться на другие сайты

В 1/8/2019 в 01:29, parovoZZ сказал:

Есть у нас функция ..., которая просто возвращает значение регистра.

Не надо делать таких функций. Все, что делается оператором присваивания, должно делаться оператором присваивания, функция тут - лишняя сущность.

В остальном согласен с предыдущим оратором - оптимизаторы нынче умные пошли... многие "неудобности" проглатывают и не морщатся.

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

Инженеры КОМПЭЛ провели сравнительное тестирование аккумуляторов EVE и Samsung популярного для бытовых и индустриальных применений типоразмера 18650. 

Для теста были выбраны аккумуляторы литий-никельмарганцевой системы: по два образца одного наименования каждого производителя – и протестированы на двух значениях тока разряда: 0,5 А и 2,5 А. Испытания проводились в нормальных условиях на электронной нагрузке EBD-USB от ZKEtech, а зарядка осуществлялась от лабораторного источника питания в режиме CC+CV в соответствии с рекомендациями в даташите на определенную модель. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Функция даст больше контроля над данными, чем макрос. Макрос лучше использовать там где с функцией не все получается так как надо.
Я бы помог компилятору и всё же добавил бы inline. В возвращении функцией одного значения не вижу ничего плохого.

Ссылка на комментарий
Поделиться на другие сайты

Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. 

Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств. Подробнее параметры и результаты тестов новой серии PLM по ссылке.

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Только что, technik-1017 сказал:

В возвращении функцией одного значения не вижу ничего плохого

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

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

Литиевые батарейки и аккумуляторы от мирового лидера  EVE в Компэл

Компания Компэл, официальный дистрибьютор EVE Energy, бренда №1 по производству химических источников тока (ХИТ) в мире, предлагает продукцию EVE как со склада, так и под заказ. Компания EVE широко известна в странах Европы, Америки и Юго-Восточной Азии уже более 20 лет. Недавно EVE была объявлена поставщиком новых аккумуляторных элементов круглого формата для электрических моделей «нового класса» компании BMW.

Продукция EVE предназначена для самого широкого спектра применений – от бытового до промышленного. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Функция практически всегда лучше. Сейчас только один регистр, а потом появятся условия, код туда полезет... особено если эта функция - интерфейс будущей библиотеки.

Учение - изучение правил. Опыт - изучение исключений.

Ссылка на комментарий
Поделиться на другие сайты

Ну вот пример. Есть такой цикл

          do
            {
                ADC_Change_in(ADC_channels[ADC_current_channel]);

                Sleep(SLEEP_MODE_ADC);

                Value_current[lsb][ADC_current_channel] = ADCL;
                Value_current[msb][ADC_current_channel] = ADCH;

            } while (ADC_current_channel--);

Sleep () определена в этом же файле.

В отдельном файле ADC.c определяю функцию:

void ADC_Change_in (uint8_t Analog_input)
{
    ADMUX = (1<<REFS1) | (0<<REFS0) | Analog_input;
}

И получаю размер кода 1232 байта. Комменчу эту функцию и вместо неё пишу в ADC.h:

#define ADC_Change_in(Analog_input)    ADMUX = (1<<REFS1) | (0<<REFS0) | (Analog_input)

Размер кода 1164 байта! Компилятор AVR/GNU C Compiler : 5.4.0. Оптимизация O3.

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

Ссылка на комментарий
Поделиться на другие сайты

Вставлю своё скромное мнение.

1. 

10 часов назад, parovoZZ сказал:

Sleep () определена в этом же файле

То есть "сон" у вас логически увязан с текущим модулем каких-то измерений, а переключение каналов измерительных логически не увязано и находится в другом файле. Я верно понял?

2. 

void ADC_Change_in (uint8_t Analog_input)
{
    ADMUX = (1<<REFS1) | (0<<REFS0) | Analog_input;
}

Правила хорошего тона потребуют от вас изменить функцию как-то так:

void ADC_Change_in (uint8_t Analog_input)
{
  if(Analog_input < VALID_AIN)
    ADMUX = (1<<REFS1) | (0<<REFS0) | (Analog_input);
}

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

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

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

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, technik-1017 сказал:

у вас функция ADC_Change_in() не inline?

Она определена в отдельном с файле. Поэтому что static, что inline, extern - компилятор её не сможет заинлайнить. Чтобы это случилось, её надо определить в заголовке, ну или в вызывающем файле. Первое меня устраивает, но возникает вопрос данного топика, второе меня категорически не устраивает).

31 минуту назад, ARV сказал:

То есть "сон" у вас логически увязан с текущим модулем каких-то измерений, а переключение каналов измерительных логически не увязано и находится в другом файле. Я верно понял?

Не совсем понял вопроса. Но отвечу. Данный цикл вызывается по сработке от таймера, чтобы обеспечить равномерную по времени оцифровку. АЦП включен в режиме свободного запуска. Я меняю канал мультиплексора и увожу ЦПУ в ADC Noise Reduction. Как только ЦПУ остановится, автоматически запустится АЦП на измерение. По окончании я выхожу из сна через обработчик прерывания от АЦП и снимаю показания. И так три раза.

37 минут назад, ARV сказал:

Правила хорошего тона потребуют от вас изменить функцию как-то так:

Когда над проектом работает несколько человек - да, несомненно. У меня же каналы определяются через #define (тупо числа из даташита) и они же заносятся в массив для возможности перебора. Если есть более изящное решение по переключению каналов, то извольте выложить=)

11 час назад, Alexeyslav сказал:

Выигрываем, конечно. Но какой ценой?

А чем я плачу? Размер кода уменьшен, скорость выполнения выше.

41 минуту назад, ARV сказал:

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

Спасибо, почитаю.

Ссылка на комментарий
Поделиться на другие сайты

Только что, parovoZZ сказал:

Не совсем понял вопроса

Если вы переключение каналов поместите в тот же файл, где и sleep() и замеры, проблема исчезнет. Я об этом.

Разбиение проекта на модули может быть по 2 основным принципам: по функционалу и по логике. По функционалу все, что касается АЦП, должно быть в одном файле-модуле, и в этом случае не понятно, с чего ИЗМЕРЕНИЕ ПРИ ПОМОЩИ АЦП у вас в одном файле, а ПЕРЕКЛЮЧЕНИЕ КАНАЛОВ АЦП в другом.

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

Только что, parovoZZ сказал:

её надо определить в заголовке

В заголовке определять функцию - дурной тон, не надо так делать.

Только что, parovoZZ сказал:

второе меня категорически не устраивает

Вот я и вопрошаю: почему?

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

30 минут назад, ARV сказал:

Вот я и вопрошаю: почему?

Удобнее переносить файлы между проектами. Можно и одной страницей писать ( как это делают поклонники ардуины) - это даже удобнее при изучении исходников.

31 минуту назад, ARV сказал:

в этом случае не понятно, с чего ИЗМЕРЕНИЕ ПРИ ПОМОЩИ АЦП у вас в одном файле, а ПЕРЕКЛЮЧЕНИЕ КАНАЛОВ АЦП в другом.

Потом через макросы унесу в ADC.h

1 час назад, ARV сказал:

А это уже в макрос хуже ложится.

через

do
{
}while(0);

должно залезть=)

Ссылка на комментарий
Поделиться на другие сайты

Вы не совсем правильно понимаете мою мысль... ну или я не могу её донести до вас понятным образом. Попробую еще раз.

Есть подход разбиения проекта на модули по периферии: adc - все, что касается АЦП, timer - все, что касается таймера, usart - все что касается USART и т.д.

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

Есть подход разбиения проекта на модули по ЛОГИКЕ АЛГОРИТМА: measure - все, что касается измерения (АЦП, таймер, который запускает АЦП, промежуточные буферы и т.п.), calculate - все, что касается бизнес-логики проекта (обработка измеренных значений, вычисление управляющих воздействий и т.п.) и так далее. Примеры надуманные, но должно быть ясно, в чем суть.

При этом вопросов, аналогичных вами описанным, не возникает в принципе. Хотя нет гарантии, что не возникнут другие вопросы :)

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

Ну почему же? Понял. Я так и делаю. И именно из-за желания заинлайнить короткие и однократно вызываемые функции и возникла эта тема.

Ссылка на комментарий
Поделиться на другие сайты

Значит, показалось :)

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

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, ARV сказал:

Есть подход разбиения проекта на модули по периферии: adc - все, что касается АЦП, timer - все, что касается таймера, usart - все что касается USART и т.д.

Вот как раз сейчас делая проект для себя небольшой, на стм32, столкнулся с проблемой как логичней сделать. Хотелось бы отсебятины добавить.

Например куб делает так, он создает функцию TIM_init, GPIO_init

То есть в TIM все что касается регистров TIM а в GPIO что касается гпио

Но это оказалось очень не удобным!

Например мне надо настроить ногу для таймера, почему она должна настраиваться в GPIO если логичней это отнести к настройке таймера? Возможно это дело привычки.

То есть я выношу в отдельный файл все что касается получения температуры, а это вариант с DMA+TIMER или просто в цикле опрос. И вот тут выходит не логично хранить настройку ноги в функции GPIO :)

 

 

Ссылка на комментарий
Поделиться на другие сайты

4 минуты назад, ARV сказал:

а с LTO-оптимизацией может попробовать и межмодульно... 

но ему ж тогда придется функцию с квалификатором inline компилировать в отдельный модуль?

Ссылка на комментарий
Поделиться на другие сайты

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

Только что, DrobyshevAlex сказал:

То есть я выношу в отдельный файл все что касается получения температуры, а это вариант с DMA+TIMER или просто в цикле опрос. И вот тут выходит не логично хранить настройку ноги в функции GPIO

Бинго! О чем я и говорил.

Я для себя применяю следующий способ, снимающий практически любой головняк в подобных случаях.

GCC имеет встроенные секции кода init0...init9, в которых можно размещать naked-функции, которые будут "вызваны" автоматически при компиляции ДО начала main. В каждом модуле я делаю такую функцию в секции с номером 6-8 (9 - это фактически main, а первые секции могут быть с еще не инициализированным стеком или не обнуленными/не проинициализированными static-переменными), где инициализирую всю периферию и т.п., необходимую для ЭТОГО модуля. В main при этом никаких init_xxxx() вообще не требуется.

Мне такой подход представляется очень удобным... хотя есть и критики.

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, ARV сказал:

В заголовке определять функцию - дурной тон, не надо так делать.

В заголовках атмела определения inline функций заменены на макросы. Значит, тема раскрыта.

Ссылка на комментарий
Поделиться на другие сайты

Это не отменяет дурного тона :)

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

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

Учение - изучение правил. Опыт - изучение исключений.

Ссылка на комментарий
Поделиться на другие сайты

11 час назад, ARV сказал:

Это не отменяет дурного тона :)

Просматриваю сейчас библиотеку LUFA - вообще не стесняются определения функции в заголовке ))

Ссылка на комментарий
Поделиться на другие сайты

Просмотрите исходник avr-libc или ядра linux - там goto и longjmp сплошняком... и тем не менее общепринято считать это дурным стилем программирования. Никто не расстреливает за это, но и в восторг не приходит. Просто имейте ввиду.

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...