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

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

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

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

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

РЕДАКТИРОВАТЬ Я думаю, что мне удалось потерять суть моего вопроса, и, возможно, это действительно два.

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

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

я ++;
может стать
ДОБАВИТЬ РЕГ, 1
AND REG, 0xFF
что не нужно.

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

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

10.08.2008 07:50:54
9 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

  3. Определения типов теоретически могут помочь переносимости, но я, например, никогда ничего от них не получил. Есть ли полезная система, которую вы можете портировать с цели 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 <int, Least, 32> не имеет оператора + перегрузка для аргумента типа класса Number <unsigned long long, Fast, 64>, кандидаты ...» I не называйте это "читабельностью". Ваши шансы на правильную реализацию этих классов-оберток невелики, и большую часть времени вы будете ждать, пока не скомпилируются бесчисленные экземпляры шаблонов.

17
17.08.2008 19:15:31
Еще один трюк, который я видел и оценил, - это использование типов FIXED и типов «USE BEST». то есть typedef unsigned char UINT8 typedef unsigned uint255; uint 255 задает диапазон значений, но позволяет указать оптимальный размер для системы
itj 13.09.2008 21:15:43
@itj: вместо uint255 используйте uint_fast8_tfrom stdint.h. Он определяется как быстрый тип, который может поддерживать 8-разрядное значение без знака. На одной платформе это может быть unsigned char. По другому это может быть просто unsigned int.
tomlogic 15.08.2010 21:17:18
Это нормально, пока ваш код не будет работать на очень ограниченном процессоре, а вы захотите или должны будете протестировать на другом процессоре, возможно, потому что тестирование и отладка на реальной цели трудны / невозможны. В этой ситуации вам нужно протестировать на своем хосте dev с переменными того же размера, что и на цели, и если вы не закодировали с определенными по размеру typedef, ваш процесс будет полностью завершен.
barny 26.12.2017 20:55:18

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

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

1
10.08.2008 08:11:40

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

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

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

7
10.08.2008 09:03:24

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

4
11.08.2008 04:29:28

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

4
20.08.2008 14:12:16
+1 за использование типов в stdint.h. Лучший способ пойти на переносимость. Если у платформы его нет, его просто создать.
tomlogic 15.08.2010 21:18:26

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

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

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

3
24.08.2008 14:48:54
Да, это консультативное правило (№ 6.3 MISRA-C 2004 и соответственно № 13 MISRA-C '98) .
ollo 10.06.2015 16:45:24

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

0
14.08.2010 03:23:24
Я думаю, это зависит от контекста. Для встроенного программирования размер очень важен, так что я и есть значения, которые не имеют значения.
itj 16.08.2010 10:01:26
@itj: Я не совсем понимаю, что вы имеете в виду. Я использую мои двухсимвольные идентификаторы типов, потому что они короткие, визуально чистые и четкие. Я не могу вспомнить какие-либо другие двухсимвольные идентификаторы, которые я использую для любых целей, которые начинаются с «s» или «u», поэтому вполне очевидно, что означают эти типы (за исключением, возможно, «ui» или «si»). в изоляции).
supercat 16.08.2010 15:42:09

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

Как ТЗ. Как уже было сказано, 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.06.2015 16:43:56

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

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

1
20.06.2016 10:07:49
Да, это одна из самых прагматичных демонстраций того, что использование явных размеров имеет большой смысл. Конечно, если вы не будете / никогда не будете проводить тестирование на ПК, то зачем вам это?
barny 24.12.2017 14:42:38