Когда следует использовать абстракцию типов во встроенных системах - PullRequest
18 голосов
/ 10 августа 2008

Я работал над несколькими различными встроенными системами. Все они использовали typedef s (или #defines) для таких типов, как UINT32.

Это хорошая техника, так как она передает программисту размер шрифта и позволяет вам лучше понимать вероятность переполнения и т. Д.

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

Итак, что должно повлиять на ваше решение о создании и применении типов для проекта?

EDIT Я думаю, что мне удалось потерять суть моего вопроса, и, возможно, это действительно два.

При встроенном программировании вам могут потребоваться типы определенного размера для интерфейсов, а также для работы с ограниченными ресурсами, такими как ОЗУ. Этого нельзя избежать, но вы можете использовать базовые типы из компилятора.

Для всего остального типы имеют меньшее значение.
Вы должны быть осторожны, чтобы не вызвать переполнение и, возможно, должны следить за использованием регистров и стеков. Что может привести вас к UINT16, UCHAR. Однако использование таких типов, как UCHAR, может добавить компилятор 'fluff'. Поскольку регистры обычно больше, некоторые компиляторы могут добавлять код, чтобы принудительно преобразовать результат в тип.

i++;
может стать
ADD REG,1
AND REG, 0xFF
что не нужно.

Так что я думаю, что мой вопрос должен был быть: -

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

Ответы [ 9 ]

16 голосов
/ 17 августа 2008

Я использую абстракцию типов очень редко. Вот мои аргументы, отсортированные в порядке возрастания субъективности:

  1. Локальные переменные отличаются от членов структуры и массивов в том смысле, что вы хотите, чтобы они помещались в регистр. На цели 32b / 64b локальный int16_t может сделать код медленнее по сравнению с локальным int, так как компилятору придется добавлять операции в / force / overflow в соответствии с семантикой int16_t. В то время как C99 определяет intfast_t typedef, AFAIK обычное int также будет вписываться в регистр, и это, безусловно, более короткое имя.

  2. Организации, которым нравятся эти определения типов, почти всегда заканчивают несколькими из них (INT32, int32_t, INT32_T, до бесконечности). Таким образом, организациям, использующим встроенные типы, лучше иметь только один набор имен. Я хотел бы, чтобы люди использовали typedefs из stdint.h или windows.h или чего-либо существующего; и когда у цели нет этого .h файла, как трудно его добавить?

  3. Typedefs теоретически могут помочь переносимости, но я, например, никогда ничего от них не получил. Есть ли полезная система, которую вы можете портировать с цели 32b на 16b? Есть ли система 16b, которая не является тривиальной для портирования на 32b цели? Более того, если большинство переменных являются целочисленными, вы на самом деле получите что-то от 32 битов на новой цели, но если они равны int16_t, вы этого не сделаете. А места, которые трудно переносить, все равно требуют ручного осмотра; прежде чем попробовать порт, вы не знаете, где они находятся. Теперь, если кто-то думает, что так легко портировать вещи, если у вас повсюду есть typedefs - когда приходит время к порту, что случается с несколькими системами, напишите скрипт, преобразующий все имена в базе кода. Это должно работать в соответствии с логикой «не требуется ручной осмотр» и откладывает усилия до момента, когда это действительно приносит пользу.

  4. Теперь, если переносимость может быть теоретическим преимуществом typedefs, удобочитаемость наверняка пойдет на ветер. Просто посмотрите на stdint.h: {int,uint}{max,fast,least}{8,16,32,64}_t. Много типов. Программа имеет много переменных; действительно ли так легко понять, какой должен быть int_fast16_t, а какой должен быть uint_least32_t? Сколько раз мы молча обращаемся между ними, делая их совершенно бессмысленными? (Мне особенно нравятся преобразования BOOL / Bool / eBool / boolean / bool / int. Каждая программа, написанная упорядоченной организацией, уполномочивающей typedefs, завалена этим).

  5. Конечно, в C ++ мы могли бы сделать систему типов более строгой, заключив числа в экземпляры класса шаблона в перегруженные операторы и прочее. Это означает, что теперь вы получите сообщения об ошибках вида «класс Number не имеет оператора + перегрузка для аргумента класса класса Number , кандидаты ...» I не называйте это "читабельностью". Ваши шансы на правильную реализацию этих классов-оберток невелики, и большую часть времени вы будете ждать, пока не скомпилируются бесчисленные экземпляры шаблонов.

7 голосов
/ 10 августа 2008

Стандарт C99 имеет ряд стандартных размерных целочисленных типов. Если вы можете использовать компилятор, который поддерживает C99 (gcc поддерживает), вы найдете их в <stdint.h> и можете просто использовать их в своих проектах.

Кроме того, во встроенных проектах может быть особенно важно использовать типы как своего рода «сеть безопасности» для таких вещей, как преобразование единиц. Если вы можете использовать C ++, я понимаю, что есть некоторые «модульные» библиотеки, которые позволяют вам работать в физических единицах, определенных системой типов C ++ (с помощью шаблонов), которые компилируются как операции с базовыми скалярными типами. Например, эти библиотеки не позволят вам добавить distance_t к mass_t, потому что модули не выстраиваются в очередь; вы на самом деле получите ошибку компилятора.

Даже если вы не можете работать на C ++ или другом языке, который позволяет вам писать код таким образом, вы можете по крайней мере использовать систему типов C, чтобы помочь вам ловить подобные ошибки на глаз. (На самом деле это было первоначальное намерение венгерской нотации Симони.) То, что компилятор не будет кричать на вас за добавление meter_t к gram_t, не означает, что вы не должны использовать такие типы. Обзоры кода будут намного более продуктивными при обнаружении ошибок модуля, чем.

4 голосов
/ 20 августа 2008

Мне нравится использовать типы stdint.h для определения системных API, в частности, потому что они явно говорят о том, каковы большие элементы. В прежние времена в Palm OS системные API-интерфейсы определялись с использованием множества «лишенных смысла» типов, таких как «Word» и «SWord», которые были унаследованы от очень классической Mac OS. Вместо этого они произвели очистку, чтобы сказать Int16, и это облегчило понимание API для новичков, особенно в связи со странными проблемами с 16-разрядными указателями в этой системе. Когда они разрабатывали Palm OS Cobalt, они снова изменили эти имена, чтобы они соответствовали именам stdint.h, что сделало его еще более понятным и уменьшило количество typedef, которыми они должны были управлять.

4 голосов
/ 11 августа 2008

Мое мнение, если вы зависите от минимального / максимального / определенного размера не , просто предположите, что (скажем) unsigned int составляет 32 байта - используйте uint32_t вместо этого (при условии, что ваш компилятор поддерживает C99).

3 голосов
/ 24 августа 2008

Я считаю, что стандарты MISRA предполагают (требуют?) Использование typedefs.

С личной точки зрения, использование typedefs не оставляет путаницы относительно размера (в битах / байтах) определенных типов. Я видел, как ведущие разработчики пытаются использовать оба способа разработки, используя стандартные типы, например int и использование пользовательских типов, например UInt32.

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

1 голос
/ 20 июня 2016

Использование <stdint.h> делает ваш код более переносимым для модульного тестирования на ПК.

Он может сильно укусить вас, когда у вас есть тесты для всего, но он все еще ломается в вашей целевой системе, потому что int внезапно только 16-битный.

1 голос
/ 10 июня 2015

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

Как уже говорил TK. , MISRA-C имеет (рекомендательное) правило для этого:

Правило 6.3 (рекомендация): вместо основных числовых типов следует использовать typedef, указывающие размер и подпись.

(от MISRA-C 2004; это правило № 13 (adv) от MISRA-C 1998)


То же самое относится и к C ++ в этой области; например. Стандарты кодирования JSF C ++ :

AV Rule 209 Будет создан файл UniversalTypes для определения всех основных Стандартные типы для разработчиков. Типы включают в себя: [uint16, int16, uint32_t и т. Д.]

1 голос
/ 10 августа 2008

Согласованность, удобство и удобочитаемость. «UINT32» гораздо более читабелен и доступен для записи, чем «unsigned long long», что является эквивалентом для некоторых систем.

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

0 голосов
/ 14 августа 2010

Возможно, я странный, но я использую ub, ui, ul, sb, si и sl для целочисленных типов. Возможно, «i» для 16 бит выглядит немного устаревшим, но мне больше нравится внешний вид ui / si, чем uw / sw.

...