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

StackOverflow https://stackoverflow.com/questions/7084

  •  08-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

  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.Каждая программа, написанная организованной организацией, требующей определения типов, изобилует этим).

  5. Конечно, в C++ мы могли бы сделать систему типов более строгой, обернув числа в экземпляры шаблонных классов перегруженными операторами и тому подобным.Это означает, что теперь вы получите сообщения об ошибках вида «класс Number<int,Least,32> не имеет перегрузки оператора+ для аргумента типа class Number<unsigned long long,Fast,64>, кандидатами являются...» I не называйте это и «читабельностью».Ваши шансы правильно реализовать эти классы-оболочки микроскопичны, и большую часть времени вам придется ждать, пока скомпилируются бесчисленные экземпляры шаблонов.

Другие советы

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

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

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

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

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

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

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

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

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

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

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

Как ТК. уже говорил раньше, МИСРА-С имеет (рекомендательное) правило для этого:

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

(из MISRA-C 2004;это Правило № 13 (adv) MISRA-C 1998 г.)


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

Правило АВ 209 Будет создан файл UniversaltPes для определения всех типов Sta NDARD для использования разработчиками.К типам относятся:[uint16, int16, uint32_t и т. д.]

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top