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

Плавный переход C -> C++ под МК

Все чаще замечаю мнения (и тут, и на иностранных ресурсах), что Си макросы в том виде, котором они есть, когда-нибудь должны быть уничтожены в C++:bye:

Типа, все в плюсах можно переписать на более праведном шаблонном метапрограммировании с контролем типов и т.д. Я бы хотел в этом убедиться.

Начну с простого. У меня есть макросы и вспомогательные макрос-функции для определения бита (бит-маски) по его номеру

#define B0  bit(0)
#define B1  bit(1)
#define B2  bit(2)
...
#define B63 bit(63)

т.е., например, B5 это будет (1u << 5).

Все эти определения B0...B63 у меня находятся в файле macros.h, который я дописываю/изменяю крайне редко. Т.е. macros.h, можно сказать, платформо-независимый (абстрактный). В начале этого заголовочника у меня подключается build_opts.h, в котором как раз я определяю некие платформо-зависимые вещи, на основе которых "рабочие" макросы разворачиваются в правильный вид. Поэтому для идентичности использования макросов при кодировании под разные архитектуры мне нужно поправить только один файл build_opts.h.

Так вот, в build_opts.h у меня задаются длины (хвосты) литеральных целых, т.к. это напрямую диктуется разрядностью системы. Для Cortex-M4, например,

#define INT_LITERAL_SUFFIX_OCT_0
#define INT_LITERAL_SUFFIX_OCT_1
#define INT_LITERAL_SUFFIX_OCT_2
#define INT_LITERAL_SUFFIX_OCT_3
#define INT_LITERAL_SUFFIX_OCT_4 ll
#define INT_LITERAL_SUFFIX_OCT_5 ll
#define INT_LITERAL_SUFFIX_OCT_6 ll
#define INT_LITERAL_SUFFIX_OCT_7 ll


И теперь, самый интересный фокус - определение макроса bit()

#define bit(num) (cond_concat_2((num) / 8 == 0, 1u, INT_LITERAL_SUFFIX_OCT_0,               \
                    cond_concat_2((num) / 8 == 1, 1u, INT_LITERAL_SUFFIX_OCT_1,             \
                      cond_concat_2((num) / 8 == 2, 1u, INT_LITERAL_SUFFIX_OCT_2,           \
                        cond_concat_2((num) / 8 == 3, 1u, INT_LITERAL_SUFFIX_OCT_3,         \
                          cond_concat_2((num) / 8 == 4, 1u, INT_LITERAL_SUFFIX_OCT_4,       \
                            cond_concat_2((num) / 8 == 5, 1u, INT_LITERAL_SUFFIX_OCT_5,     \
                              cond_concat_2((num) / 8 == 6, 1u, INT_LITERAL_SUFFIX_OCT_6,   \
                                cond_concat_2((num) / 8 == 7, 1u, INT_LITERAL_SUFFIX_OCT_7, \
                                  1ull)))))))) << (num))


Здесь по номеру бита вычисляется номер физического октета, а по нему, в зависимости от архитектурных особенностей (литеральных хвостов в данном случае), получаемая битовая маска правильного типа. Сам фокус в использовании вспомогательного макроса "тернарно-условной конкатенации" cond_concat_2(cond, a, b, c), который на этапе компиляции вычисляет условие cond, если оно истинно, делает конкатенацию токенов a##b, и это будет результатом этого макроса, а в противном случае (если cond == 0), cond_concat_2() вернет токен c.

Казалось бы, в реализации cond_concat_2() можно было применить операцию тернарного условия ?:, однако в таком случае bit() с любым параметром имел бы тип ull, что совершенно некорректно. Я обратился к документации на различные компиляторы и обнаружил, что в широко распространенном GCC и CLang разработчики об этом позаботились, реализовав макрос __builtin_choose_expr(). Это прямо то, что нужно! Примечание: жаль, конечно, что на текущий момент разработчики не разрешают вносить в невыбранное выражение синтаксически неверное выражение (либо несуществующий токен препроцессора), хотя, вроде как, обещались, что подумают над этим.

Значит, вот cond_concat_2()

#define cond_concat_2(cond, a, b, expr2) __builtin_choose_expr(cond, concat_2(a, b), expr2)


С реализацией склейки concat_2(), надеюсь, все понятно, это стандартный двойной разворот

#define concat_2(...)        _concat_2(__VA_ARGS__)
...
#define _concat_2(a, b, ...) a##b


Теперь я точно знаю, что bit(30) развернется в (1u << 30) и это будет правильно, а bit(60) развернется в (1ull << 60) и при использовании в различных выражениях со смешанными целыми типами данных у меня не будет вылезать куча побочных варнингов из-за неявного приведения типов и т.д. Очень удобно. Да, можно было определить Bx напрямую склейкой 1u с нужным хвостом, но я посчитал это громоздким, можно ошибиться, да и у меня есть и другие макросы, которые построены по похожему принципу, т.к. так читаются легче.

Как такое реализовать в C++? Чем, по сути, будет являться определение Bx? Это будет некий объект класса или как?

Поделиться сообщением


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

3 hours ago, Arlleex said:

Как такое реализовать в C++?

Если именно такое, то это будут такие же костыли.

Я бы исходил из задачи, а не решения, что мол надо на плюсах и все тут.

Какая вообще задача? В общем

 

К слову, работу с пинами сделал иначе, на плюсах конечно, никаких макросов, но есть крохотный оверхед по озу, но мне это до лампочки в моих задачах. Читаемость куда ценнее ))

 

Поделиться сообщением


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

11 минут назад, Forger сказал:

Если именно такое, то это будут такие же костыли.

Не ну а какое еще)) Мы ж в программах не абстрактными классами машем, в конце концов. Просто меня порой удивляет мнение некоторых товарищей, мол "так никто не пишет", "в C++ все проектируется в других категориях мышления" и другой уже невыносимый бред)) Такое чувство, что код там не пишется, а философствуется))

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

Цитата

Я бы исходил из задачи, а не решения, что мол надо на плюсах и все тут.

Задача, как всегда, донельзя проста: написать некие тривиальные синтаксические "облегчалки", которыми потом сыпать в реальном коде. Т.е. действительно от макросов вообще можно уйти.
 

Цитата

Впрочем именно так у себя и делаю. На плюсах, есть крохотный оверхед по озу, но мне это до лампочки в моих задачах. Читаемость куда ценнее ))

Это как? Откуда оверхед? У меня на этих макросах оверхед (еще и по ОЗУ) == 0, его просто нет. На любых уровнях оптимизации.

Поделиться сообщением


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

3 hours ago, Arlleex said:

 Просто меня порой удивляет мнение некоторых товарищей, мол "так никто не пишет", "в C++ все проектируется в других категориях мышления" и

ну вообще то так и есть, если речь про старое доброе ООП

 

3 hours ago, Arlleex said:

Это как? Откуда оверхед? У меня на этих макросах оверхед (еще и по ОЗУ) == 0, его просто нет. На любых уровнях оптимизации.

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

Мне глубоко до лампочки мизерные экономии на озу, когда его тут вагон и маленькая тележка )

Для хранения объекта нужна память, а чтобы пин дрыгался быстро, объект должен иметь указательно на порт и номер пина порта.

Ранее я это не раз расписывал и не хочу уже повторяться. Но мне лично такой подход чертовски удобен. Т.к. в коде нет никакой привязки к железу. Это очень удобно прыгать с камня на камень практически не меняя код.

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

А макросы практически никогда более не использую. За очень редким исключением.

Вот например как использую пины, тут SPI софтовый (аппаратный кстати аналогично) и он общий на все камни, как и SX1261, тоже никак не зависит от железа:

class ThreadRadio : public OS::Thread<kRadioStackSize>
{
  public:
    ThreadRadio() : OS::Thread<kRadioStackSize>("Communication.ThreadRadio") {}

  private:

    virtual void initialize() final;
    virtual void body() final;
    ....
  
    Hardware::Pin<PA1> 	pinRST;
    Hardware::Pin<PA2> 	pinBUSY;
    Hardware::Pin<PA5> 	pinSCK;
    Hardware::Pin<PA7> 	pinMOSI;
    Hardware::Pin<PA6> 	pinMISO;
    Hardware::Pin<PA4> 	pinCS1;

    SoftSPI softSPI {pinSCK, pinMOSI, pinMISO, pinCS1};
    SX1261 radio {softSPI, pinBUSY, pinRST};

Подобную схему кстати использую в ардуинах, но мне их подход не очень по душе

 

Макрос тут только 

Hardware

Его достаточно глобально объявить в свойствах проекта, в коде это не фигурирует. И все, проект собирается под камень на другом железе и уже с его библиотеками.

Поделиться сообщением


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

12 минут назад, Forger сказал:

ну вообще то так и есть, если речь про старое доброе ООП

Это все, конечно, хорошо, но, как говорится, не верю:wink: Потому что когда пишешь банальный код, типа обычный линейный код с логикой, ООП-шники кричат, что ты все не в стиле ООП делаешь. На вопрос - а где в этот ваш ООП реальный код вставлять, просто повторяют что ты пишешь не так, и гуру пишут классы и всякое там прочее. Такое чувство, что в плюсах код вообще писать не надо. Где написание драйверов? Где написание логики? Где все то, что пишут Си-программисты?
 

Цитата

у меня каждый пин

Ну вообще я не про пины)) А вообще. У меня эти биты B0...B63 используются очень часто в драйверах периферии и т.д. Вот и стало интересно, что плюсы могут предложить в альтернативу как более правильный и гибкий вариант.

Поделиться сообщением


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

21 minutes ago, Forger said:

Подобную схему кстати использую в ардуинах, но мне их подход не очень по душе

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

Поделиться сообщением


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

1 hour ago, Arlleex said:

У меня эти биты B0...B63 используются очень часто в драйверах периферии и т.д.

у меня вся эта аппаратно зависимая "тряхомуть" спрятана глубоко в библиотеках к периферии. Там хороши любые средства, чтобы все работало максимально быстро. В целом использую только h файлы о производителя, все остальное через регистры.

Тут главное - продумать интерфейс с этой библиотекой. А что внутри - вторично )

Сразу не рождается правильный и удобный интерфейс, он 'проявляется" от проекта к проекту. Так библиотека постепенно дополняется новым функционалом и постепенно оптимизируется.

Сразу это делать абсурд - все равно будет мимо кассы.

 

1 hour ago, x893 said:

В ардуино такой же подход.

Так я про это и говорю, просто их вариант с макросами мне как-то не очень. Там вообще смесь голого си и плюсов, причем порой очень причудливая. Речь конечно про сторонние библиотеки .

И там нет многозадачности "в стоке".

Поделиться сообщением


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

30 minutes ago, Forger said:

Так я про это и говорю, просто их вариант с макросами мне как-то не очень. Там вообще смесь голого си и плюсов, причем порой очень причудливая. Речь конечно про сторонние библиотеки .

И там нет многозадачности "в стоке".

Ну там народу много, каждый строчит как хочет. Кухаркам пофиг, что там под "капотом"

Поделиться сообщением


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

On 3/16/2023 at 1:16 AM, Arlleex said:

Что-то читал я про union-ы в C++ и пришел к выводу, что в комитете дурь очень забористая.
Оказывается, нельзя в плюсах репрезентовать битовое представление, как это делалось обычно
И выход из ситуации через memcpy(), placement-new [] и иже им подобным костылям с трехэтажным монструозным синтаксисом.
Почему Страуструп и Ко просто не взял и не скопировал у Си?:fool:

Страуструп взял C и добавил к нему фронт-энд прекомпилятор, превращавший си с классами в обычный си код. Он так и назывался, CFront.
VC6 (1998) (а также Comeau) имел опцию показать промежуточный этап компиляции C++ в виде C кода.

On 3/16/2023 at 2:34 AM, Arlleex said:

Он не будет ругаться, а по стандарту вышеуказанный хук слева с union-ом оборачивается UB. О как.

Компилятор у меня ARM CLang (keil-овский v6). Он, конечно, код пока что генерит правильно, однако я уже достаточно вкурил, чтобы понять, что плюсоводы такое использование union не одобряют. Одобряют только (и только лишь) хранение разных типов внутри одного кусочка памяти. Обращаться на чтение можно только к элементу, тип которого совпадает с типом элемента на последнюю запись значения в union. Это так называемая установка активного члена union-а. Совсем одурели, в америке своей.

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

On 3/16/2023 at 2:47 AM, Arlleex said:

Никакие касты (ни static, ни reinterpret) не влияют на UB при доступе к члену union другого типа. Это я тоже почитал((
Получается, по правилам C++ я так сделать не могу. И как тогда могу?


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

Ключевая мысль:  когда описывается стандарт языка, намеренно ничего не предполагают о "платформе" (машине на которой исполняется)
и ничего не предполагают о компиляторе. Даже о формате чисел и представлении нуля (null pointer) ранее не делалось никаких предположений (хотя например позже сделаны оговорки о предеставляениях при конверсии).
Для указателей например есть предположение только о совпадении == или не-совпадении !=, предположения о монотонном возрастании адресов вообще говоря нет,
как нет (или не было на уровне стандарта какого-либо года) даже представления о непрерывности памяти в ряде случаев.
Здесь важная оговорка - стандарт C++ постоянно изменяется и некоторые утверждения верны для ранних стандартов и неверны для поздних.

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

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

И так как они не описывают гарантированное (стандартизованное) поведение  это и называется undefined. В смысле undefined в стандарте.
Это отсутствие поведения гарантированного на уровне стандарта и только стандарта. Оно же,  "отсутствие поведения заданного/описанного стандартом", или "данный стандарт это не описывает и не гарантирует". Внезапно, но и понятие UB описано в стандарте. читаем defns.undefined —
behavior for which this document imposes no requirements

Расширять эту трактовку на "моя конкретная программа на конкретной платформе обязательно здесь потерпит крах" или гарантированно поведет себя непредсказуемо - неверно.
К сожалению, UB само по себе слишком широкое понятие. (UB и так это undefined или unspecified behaviour)
Есть неопасный UB, а есть опасный. Что где и как, на что наплевать, а где насторожиться поможет либо опыт, либо статический анализатор кода, либо практические эксперименты на конкретной платформе.  Конечным мерилом явлется только практика, в этом смысле оглядываться на каких-то сферических коней в вакууме пишущих о каких-то сферических конях в вакууме с какими-то сферическими запретами в виду сферическо-вакуумного UB...  Ну такое...
Надо брать и извлекать для себя лучшее. В этом смысл всей этой затеи.

 

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

Поделиться сообщением


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

13 часов назад, Forger сказал:

Мне глубоко до лампочки мизерные экономии на озу, когда его тут вагон и маленькая тележка )

...

тут SPI софтовый

Печалька....

На ПК такой подход (возобладавший последнее время) уже привёл к тому, что простейшая программа занимает десятки-сотни МБ ОЗУ. И еле шевелится на CPU, если он не последний, топовый.

И уже почти никого не удивляет, что браузер, со всего двумя открытыми вкладками с этим форумом (где почти нет графики и какого-то другого тяжёлого контента), занимает сейчас 550МБ(!) ОЗУ и сжирает 3%(!) времени на почти топовом CPU в режиме просто просмотра страниц! Если бы кому сказали лет 25 назад, что просмотр всего двух не особо больших массивов текста, будет требовать таких конских ресурсов - никто бы не поверил тогда. Теперь же это - норма.  :sad:

20 часов назад, Arlleex сказал:

Теперь я точно знаю, что bit(30) развернется в (1u << 30) и это будет правильно, а bit(60) развернется в (1ull << 60) и при использовании в различных выражениях со смешанными целыми типами данных у меня не будет вылезать куча побочных варнингов из-за неявного приведения типов и т.д.

Имхо: Вы излишне усложняете. Можно проще:

#define B0  0x00000001ul
...
#define B31 0x80000000ul
#define B32 (1ull << 32)
...
#define B63 (1ull << 63)

Поделиться сообщением


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

30 minutes ago, jcxz said:

Печалька....

На ПК такой подход (возобладавший последнее время) уже привёл к тому, что простейшая программа занимает десятки-сотни МБ ОЗУ. И еле шевелится на CPU, если он не последний, топовый.

Ничего печального в этом не вижу.

Это связано не с неким "подходом", а опытом прогеров и требованиями заказчика к срокам и функционалу.

А что еле шевелится - это вопрос к программистам или античному компу где все это запускают ))

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

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

 

30 minutes ago, jcxz said:

Если бы кому сказали лет 25 назад...

если бы кому сказали лет 150 назад что люди будут летать в небе ... 

если постоянно бодаться за "экономию на спичках", то с таким подходом далеко не уедешь ))

Поделиться сообщением


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

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

У меня лично ногодрыг работает так же как быстро как и на макросах.

Какой ногодрыг? SPI ногодрыгом? А ничего, что он полностью занимает CPU?

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

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

А у меня скорее вопрос к программистам у которых "компиляторы не очень правильно порой разворачивают макросы". Точнее - к их квалификации.  :unknw:

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

если постоянно бодаться за "экономию на спичках", то с таким подходом далеко не уедешь ))

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

 

PS: Если не давать себе труда напрягать мозги там, где кажется "и так сойдёт", то в конце-концов они совсем закиснут и атрофируются. И когда встретится задача, которая потребует их напрячь, то напрягать будет уже нечего.  :unknw:

Никого конкретно не имел в виду, если что....

Поделиться сообщением


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

12 minutes ago, jcxz said:

А ничего, что он полностью занимает CPU?

Ничего. Т.к. в данном случае обмен ни разу не интенсивный и запас по производительности у камня и так чудовищный.

 

15 minutes ago, jcxz said:

Точнее - к их квалификации. 

А у меня нет вопросов к квалификации тру поклонников макросов. Это их выбор, их решение. Для меня с-макросы давно пройденный этап, не зацикливаюсь.

 

13 minutes ago, jcxz said:

так и сейчас - почти столько же ждать приходится.

У меня компы работают бодро, все. Раньше компы были намного задумчивее. У большинства компов достаточно заменить античный HDD на любой SSD и он сразу оживает, даже полный хлам.

 

16 minutes ago, jcxz said:

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

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

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

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

 

 

 

Поделиться сообщением


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

16 минут назад, Forger сказал:

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

"Проворная" молодёжь, это видимо та, которая не смогла толком освоить даже макросы. Раз у неё "компиляторы не очень правильно порой разворачивают макросы".  :sarcastic:

А догонять мне никого не надо - вся та "проворная молодёжь" дальше ногодрыга как правило не ушла. Что и наблюдается в подавляющем большинстве тем.

И для решения элементарной задачи, этой молодёжи нужен не менее чем Cortex-A с тактовой в гигагерц. Так как "не интересно зацикливаться на пустой оптимизации".

16 минут назад, Forger сказал:

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

Тогда с вами всё ясно. Потому сразу и написал: "печалька".  :cray:

Поделиться сообщением


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

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

Значит, как и раньше, буду потихоньку сам смотреть по ситуации))

Поделиться сообщением


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

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

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

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

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

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

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

×
×
  • Создать...