Комментарии 42
Автору респект! Интересно и схемотехникам от программирования и программистам от схемотехники.
>>Представители старой школы являются экспертами в области как программного, так и аппаратного обеспечения. Их инструменты — язык C и ассемблер. Их главная цель — выжать все из каждого байта, достичь максимальной производительности кода при минимальном использовании памяти

Вот чего я не понял, когда начал писать под AVR, это тотальное стремление разработчиков SDK заменить нормальные понятные имена на нечитаемые аббревиатуры: GIMSK, MCUCR, MCUSR и т.п. От длины имени переменной размер бинарника ну никак не зависит
Наверное «тяжёлое» наследие мнемокода ассемблера. Там всё в 3 или 4 буквы расписано. Регистрам «повезло» чуть больше.
Ну ещё в каждом даташите есть раздел сокращений где каждое расписано что и как. Ну или по ходу текста тоже есть разъяснения.
А кто с «камнями» каждый день работает, тот все сокращение наизусть помнит (или помнит куда смотреть и освежить память).
Позволю себе предположить, что данные аббревиатуры не инициатива разработчиков программного инструментария, а следование именованию, предложенному разработчиками железа. Наверное было бы странно видеть что-то типа Sleep_Mode_Control_Register в названии регистра, который у схемотехников называется просто SMCR.
Рискну предположить, что такие названия лучше смотрятся в здоровенной таблице в начале даташита с отображением регистров на адреса памяти, на диаграммах изменения состояния регистров (тех, где показано, какие биты в какой момент меняются при приходе какого-нибудь сигнала), логических схемах отдельных блоков, и еще, может быть, в трехэтажных выражениях с побитовыми операциями в коде.

С другой стороны, какой-нибудь «младший бит выбора режима таймера номер три» — вот как его красиво назвать?
Битовые операции это отдельная история. В SDK вроде предусмотрели макрос для установки битов (чтобы не писать бесконечные сдвиги << 1), но кто-то очень «дружественный к разработчикам» назвал его _BV. К черту читаемость кода, главное покороче. Почему было не назвать BIT_VALUE? Или немного модифицировать и сделать SET_BIT/UNSET_BIT?
В прилагаемом коде (в хидере avr_misc) для этой цели объявлен шаблон вроде:
template
<
    unsigned bitnum,
    typename ValueType = uint8_t
>
struct bit
{
    static const ValueType value; // по факту value = (ValueType(1) << bitnum);
};

При использовании мы и получаем требуемое
    bit<0>::value
    ...
    bit<7>::value

Ну и конечно вместо целочисленного смещения бита можно использовать соответствующие мнемоники из Atmel-овских хидеров.
Как утверждают классики-программисты, такое определение имеет преимущество перед макросами. Действительно, компилятор сообщит вам если вы случайно сдвинули бит за пределы размера заданного типа.
Спасибо, да, видел такую структурку, действительно удобно. Также видел удобную обертку, позволяющую писать еще лучше: РЕГИСТР.БИТ =
Это очень удобно, потому что совпадает с даташитом — и названия регистров, и названия битов.
Вот и приходится прогать постоянно заглядывая в даташит, запомнить полсотни очень похожих аббревиатур непросто, ошибиться — запросто.
Можно решить проблему самостоятельно, набрав полсотни #define с желаемыми именами.
Таки да, особо бесит когда и в даташите — мелким смазанным шрифтом, табличка в два стобца — большое поле имени регистра — и там в центре 4 буквы в огромной клеточке — они страницы pdf-ки экономили что ли? ил чернила в картридже?
Т.е эволюционно это понятно откуда взялось, но давно бы пора переключиться…

Особо это напрягает, когда это полу-хобби, полу-по-работе и возвращаешься к этому пару-тройку раз в год на недельку.
Эта проблема решается очень легко. #define новое_удобное_название_вещи = СТНЗВ в отдельном файле, и подключить его в начале проекта.
А вот что дальше потом с этим делать…
Спасибо конечно, С/С++ тут думаю все неплохо знают. "=" там у вас лишнее.
#define имя_макроса последовательность_символов

Разве нет?
Я же говорю что зря в стандарт такое приняли, ненаглядно получается.
А так-то да, постоянно страдаю.
Ну, может и не зря, можно задефайнить что-нибудь содержащее "=" а так бы путаница была.

Опять же бывают #define'ы без подстановки, что-то типа #define NO_DEBUG
А так нельзя задефайнить нечто начинающееся с пробела…
Скорей всего это пустой дефайн, т.е. присваивает пустое множество.
Спасибо за замечательную статью, в данной области их очень не хватает. Эх, когда же у меня руки дойдут плотно заняться С++ на MCU.
Да что может быть проще — берёте Arduino IDE и вперёд.

Бытует почему-то расхожее мнение что под Ардуино пишут на каком-то мистическом языке «wiring». На самом деле это настоящий честный C++ с шаблонами, классами и всем остальным, что поддерживает компилятор avr-gcc.
У меня руки не доходят до серьёзного и глубокого изучения С++, а это не «берёте Arduino IDE и вперёд».
Откровенно говоря начал было волноваться, когда увидел цикл в функции записи в порт. Когда дочитал до списков типов, успокоился, да это действительно соответствует теме моей статьи. Аналогичная проблема имеет место с ЖКИ тачскринами, при использовании покупного дисплея контакты шины данных устройства также оказываются раскидаными по разным битам разных портов. При использовании стратегий мы могли бы получить примерно следующий интерфейс:
template
<
    class pin_ctrl,
    class device = LCD_C505,
    class orient = tftcd::ORIENTATION<PORTRAIT,240,320>,
    ...
    class dbg = NO_DEBUG
>
struct TFT_LCD;

Шаблонный параметр device скрывает все детали, специфические для конкретной модели дисплея, их существует множество и для каждой определен особый порядок инициализации как минимум. Параметр orient определяет ориентацию дисплея и позволяет существенно оптимизировать функции, связанные с расчетом координат.
А вот единственная вещь, которая является платформенно зависимой, это pin_ctrl — стратегия, которая скрывает всю механику работы с шиной данных устройства. На роль именно этой стратегии годится шаблон описанный в приведенной Вами статье.
Главный же класс TFT_LCD занимается собственно отрисовкой линий, окружностей и шрифтов.
Спасибо за статью. Такое надо было на хабр в песочницу постить, а не сюда.
Инвайт на хабре дает полноценного юзера и на ГТ. Или я не прав?
Валера, я рад, что ты добрался до написания данного материала. Прочитав четверть статьи, понял кто автор, а ник, которым она подписана, не оставил сомнений.
Здесь мы опять полагаемся на компилятор, который успешно выполняет встраивание (inlining) и удаление тел пустых функций. При сборке релизной версии кода, весь неиспользуемый код будет удален.

Интересно. И как вы представляете себе удаление следующего кода:

extern volatile int somevar;

dbg::debug("Somevar=%d\n", somevar);


?
Я все-таки предположу, что пустая функция типа
void inline debug(const char*, type_of_somevar)
{
}

которая игнорирует аргументы, таки же будет удалена. Здесь print наверное имелся в виду, а не debug.
Однако ничто не мешает это проверить.
Или возможно я не понял Вашего вопроса?
Да, речь шла о функции print(), но суть в том, что переменная somevar объявлена как volatile и компилятор не может выкинуть обращение к ней не ломая семантику.
Стало быть правильно я Вас понял. Не возьму на себя смелость быть категоричным, однако мне все-таки кажется, что если внутри функции аргумент (каким бы он ни был) не используется, то это семантику не меняет.
По крайней мере примерно на таком утверждении основываются всяческие оптимизации компиляторов.
К сожалению, обращение к переменной будет оставлено, даже если все остальное тело функции будет исключено. Именно поэтому ничего лучше старых недобрых макросов для оборачивания дебажного вывода пока не предложено.
В принципе, если следовать этой логике, то как только мы обратились к volatile, мы уже нарушили семантику. Тогда уже при смене честной функции print на пустую и обратно семантика не меняется. А вот последующее комментирование (или исключение любым способом) функции исключает и обращение к переменной, что как раз семантику нарушает.
Идеальных вещей в мире не существует и приходится всегда помнить об ограничениях используемого инструмента.
В любом случае, спасибо Вам за важное замечание, к сожалению, без соответствующего тестирования и анализа я не готов обсуждать этот вопрос.
Спасибо за отличный список литературы. Который, видимо, никто не читает)
Вероятно Вы имеете в виду пункт 11? Вначале он появился как шутка, потом я решил оставить его в качестве маркера прочитали / не прочитали.
Вы первый, поздравляю!
> Автоматическая генерация методов, неявное создание временных объектов могут привести к ощутимому снижению производительности результирующего кода
А чем автоматическая генерация методов уменьшает производительность?

Давно использую C++ на микроконтроллерах и не вижу никаких проблем. Правда, не на AVR и 8 бит, а Cortex-M3 и C2000. Места, в которых может снизиться производительность довольно очевидны. Если не уверен в чём-то, то смотрю что генерит компилятор и\или меряю производительность в конкретном месте. После нескольких таких подходов становится понятно, что можно, а что нельзя. Вобщем, это я к тому, что не надо бояться, надо анализировать и сопоставлять с задачей.

С шаблонами пробовал сам раньше. По моим ощущениям, читабельность падает, а требования к новым программистам повышаются.
Очень правильное замечание. Должен признаться, мне пришлось одновременно работать над двумя версиями данной статьи. Русскоязычная версия находится перед вами, англоязычная, при благоприятном стечении обстоятельств, скоро тоже может стать доступной.
Говоря «производительность» (что у нас обычно воспринимается как скорость исполнения), я имею в виду значение слова «performance», которое имеет несколько более широкий смысл и в данном случае разумнее было бы применить слово «эффективность».
Спасибо Вам, видимо имеет смысл внести соответствующее исправление.
Что касается читабельности, зачастую код, в котором используются шаблоны, оказывается более читабельным. Разработка шаблонного кода штука действительно не простая, приходится иметь дело с очень недружелюбным синтаксисом, а вот для использования больших усилий не требуется, STL — хороший пример.
Еще раз спасибо за Вашу внимательность.
Статья хороша. Люблю такое, спасибо.

Однако, пришло время придираться.

Сначала общее: такое чувство, будто статья написана году в 2008-2009. Стандарту C++11 уже как бы так лет пять, поэтому вместо type_selector можно использовать std::conditional, вместо enum можно использовать enum class, и ещё можно спокойно говорить, что static_assert давно есть в языке C++, поэтому с диагностическими ошибками проблем чуть меньше (но они всё равно есть, и тут уместнее было бы сослаться на никак не принимаемые концепты).

В языках наподобие Питона функция может существовать в единственном экземпляре. Будучи языком со строгой типизацией, C++ потребует отдельного экземпляра функции для каждого использованного с ней типа.

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

Однако, это не единственный способ реализации шаблонов. Можно таскать с собой этакий аналог vtable, словарь указателей на функции сравнения, проверки на равенство, и так далее, и тогда можно будет обойтись одной версией функции. Это уже подход Python, который динамически (относительно) строго типизирован, и подход, например, Haskell, который имеет строжайшую статическую типизацию. Haskell умеет мономорфизировать горячие функции, впрочем, через {-# SPECIALIZE #-}, например.

Строгая типизация C++ потребует в качестве параметров указания значений, точно соответствующих объявленным типам данных.

Это требование легко ломается кастом, никто не помешает вам написать
YourClass<static_cast<YourEnum> (42)> instance;
Не скажу про IAR, но gcc поддерживает avr'ки в последних релизах в том числе.
Спасибо, порадовали. Честное слово, именно на этот эффект я очень надеялся. Первая пена прошла, рабочая неделя закончилась и настало время для вдумчивого прочтения-осознания.
По поводу типизации спорить не буду, она конечно же статическая, она же и строгая, в конце концов говорим мы здесь о C++ и упоминаем C. От кастинга enum-ов лично я бы воздержался, но удерживать кого-либо еще, не в праве. А вот по поводу причины отчего это все выглядит а-ля 2008. Причина видимо та же, что и у ребят, имена которых вы видите в списке литературы. Портируемость или как минимум стремление к ней. На моей рабочей машинке до сих пор живет win XP с 2008-й студией, там я бываю редко, а если и бываю, то лишь с одной целью — собрать какой-нибудь очередной кусок кода, который должен быть исполнен/продемонстрирован за пределами моего рабочего пространства.
Так что придирайтесь конечно, как бы мы ни старались, всегда еще остается над чем работать.
А за теплое слово спасибо, действительно приятно.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.