Почему stdatomic.h содержит atomic_uint_least16_t и atomic_uint_fast16_t, но не atomic_uint16_t?

stdatomic.h, похоже, содержит atomic_uint_least16_t и atomic_uint_fast16_t, которые являются _Atomic версиями _ 5_ типов uint_least16_t и uint_fast16_t, но не содержит atomic_uint16_t. Почему?


Для получения справочной информации из проекта N1548:

7.18.1.1 Целочисленные типы точной ширины

1 Имя typedef intN_t обозначает целочисленный тип со знаком шириной N, без битов заполнения и представление с дополнением до двух. Таким образом, int8_t обозначает такой знаковый целочисленный тип шириной ровно 8 бит.

2 Имя typedef uintN_t обозначает беззнаковый целочисленный тип с шириной N и без битов заполнения. Таким образом, uint24_t обозначает такой беззнаковый целочисленный тип шириной ровно 24 бита.

3 Эти типы не являются обязательными. Однако, если реализация предоставляет целочисленные типы с шириной 8, 16, 32 или 64 бита, без битов заполнения и (для подписанных типов), которые имеют представление с дополнением до двух, она должна определять соответствующие имена typedef.

7.18.1.2 Целочисленные типы минимальной ширины

1 Имя typedef int_leastN_t обозначает знаковый целочисленный тип с шириной не менее N, так что ни один знаковый целочисленный тип с меньшим размером не имеет по крайней мере указанной ширины. Таким образом, int_least32_t обозначает целочисленный тип со знаком шириной не менее 32 бит.

2 Имя typedef uint_leastN_t обозначает беззнаковый целочисленный тип с шириной не менее N, так что ни один беззнаковый целочисленный тип с меньшим размером не имеет по крайней мере указанной ширины. Таким образом, uint_least16_t обозначает беззнаковый целочисленный тип шириной не менее 16 бит.

3 Требуются следующие типы:

int_least8_t
int_least16_t
int_least32_t
int_least64_t
uint_least8_t
uint_least16_t
uint_least32_t
uint_least64_t

Все остальные типы этой формы не являются обязательными.

(и так далее, чтобы включить типы int_fastN_t / uint_fastN_t и т. д.)

В пункте 3 стоит выделить:

Однако, если реализация предоставляет целочисленные типы с шириной 8, 16, 32 или 64 бита, без битов заполнения и (для подписанных типов), которые имеют представление с дополнением до двух, она должна определять соответствующие имена typedef.

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

Типы atomic_ для <stdatomic.h> также перечислены в N1548] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf#page=297) (воспроизведено ниже), но это не требует соответствующего требования что если у реализации есть int16_t, то есть atomic_int16_t --- это суть моего вопроса.

7.17.6 Атомарные целые числа и типы адресов

1 Для каждой строки в следующей таблице имя атомарного типа объявлено как соответствующий прямой тип.

Atomic type name         Direct type
----------------         -----------
atomic_char              _Atomic char
atomic_schar             _Atomic signed char
atomic_uchar             _Atomic unsigned char
atomic_short             _Atomic short
atomic_ushort            _Atomic unsigned short
atomic_int               _Atomic int
atomic_uint              _Atomic unsigned int
atomic_long              _Atomic long
atomic_ulong             _Atomic unsigned long
atomic_llong             _Atomic long long
atomic_ullong            _Atomic unsigned long long
atomic_char16_t          _Atomic char16_t
atomic_char32_t          _Atomic char32_t
atomic_wchar_t           _Atomic wchar_t
atomic_int_least8_t      _Atomic int_least8_t
atomic_uint_least8_t     _Atomic uint_least8_t
atomic_int_least16_t     _Atomic int_least16_t
atomic_uint_least16_t    _Atomic uint_least16_t
atomic_int_least32_t     _Atomic int_least32_t
atomic_uint_least32_t    _Atomic uint_least32_t
atomic_int_least64_t     _Atomic int_least64_t
atomic_uint_least64_t    _Atomic uint_least64_t
atomic_int_fast8_t       _Atomic int_fast8_t
atomic_uint_fast8_t      _Atomic uint_fast8_t
atomic_int_fast16_t      _Atomic int_fast16_t
atomic_uint_fast16_t     _Atomic uint_fast16_t
atomic_int_fast32_t      _Atomic int_fast32_t
atomic_uint_fast32_t     _Atomic uint_fast32_t
atomic_int_fast64_t      _Atomic int_fast64_t
atomic_uint_fast64_t     _Atomic uint_fast64_t
atomic_intptr_t          _Atomic intptr_t
atomic_uintptr_t         _Atomic uintptr_t
atomic_size_t            _Atomic size_t
atomic_ptrdiff_t         _Atomic ptrdiff_t
atomic_intmax_t          _Atomic intmax_t
atomic_uintmax_t         _Atomic uintmax_t

2 Семантика операций над этими типами определена в 7.17.7.

3 Тип atomic_bool предоставляет атомарное логическое значение.

4 Тип atomic_address обеспечивает атомарные операции void *. Единица сложения / вычитания - один байт.

5 ПРИМЕЧАНИЕ. Представление атомарных целочисленных и адресных типов не обязательно должно иметь такой же размер, как их соответствующие обычные типы. По возможности они должны иметь одинаковый размер, так как это упрощает перенос существующего кода.


person Jason S    schedule 12.07.2020    source источник


Ответы (2)


Этот список специализированных атомарных типов существует только из-за исторической случайности, когда они были предназначены для обеспечения совместимости с C ++. И они были предназначены только для предоставления интерфейсов для обязательных целочисленных типов. Ни один из uintXX_t типов не является обязательным, поэтому они не включены.

(Эта цель была немедленно разбавлена ​​добавлением atomic_[u]intprt_t, где [u]intptr_t не обязательны, но это, вероятно, уже другая история.)

person Jens Gustedt    schedule 12.07.2020
comment
так что uint_least16_t и uint_fast16_t требуются, но uint16_t не требуется ?! - person Jason S; 13.07.2020
comment
Да, точно. Псевдонимы типов с фиксированной шириной требуются только в том случае, если платформа имеет целочисленный тип точно соответствующей ширины. - person Jens Gustedt; 13.07.2020
comment
Я не понимаю, почему атомные типы не отражают типы stdint.h - например, если требуется uint32_t, почему не atomic_uint32_t? - person Jason S; 13.07.2020
comment
(отредактировал свой вопрос с некоторыми цитатами из проекта стандарта N1548) - person Jason S; 13.07.2020
comment
uint32_t вообще не требуется, только если платформа имеет целочисленный тип с ровно 32 битами значений и без битов заполнения. - person Jens Gustedt; 14.07.2020
comment
... что и делают многие встроенные процессоры, а также uint8_t и uint16_t. - person Jason S; 14.07.2020
comment
И я понимаю, что uint32_t вообще не требуется; Я имею в виду, что если uint32_t требуется из-за параграфа 3 7.18.1.1, почему atomic_uint32_t не также требуется? - person Jason S; 14.07.2020
comment
Думаю, потому что никого не волновало. Нет особых причин использовать любой из этих типов. Вместо этого используйте _Atomic(uint32_t). - person Jens Gustedt; 14.07.2020

Я могу только догадываться, но если вы можете реализовать атомарный доступ только к вещам, размер которых превышает uint16_t, то реализация атомарного доступа к uint_least16_t и uint_fast16_t всегда может быть выполнена путем определения типов соответственно, в то время как атомарный доступ к uint16_t может быть просто невозможен с доступным оборудованием . И вам не нужно ничего в стандарте, что не может быть реализовано.

person gnasher729    schedule 12.07.2020
comment
uint16_t вообще не гарантируется. Думаю, в этом проблема. _Atomic uint16_t не обязательно будет иметь тот же размер, что и uint16_t, поэтому конечно, реализация может иметь только 32-битные атомарные операции, но каким-то образом все еще иметь uint16_t и реализовывать _Atomic uint16_t путем заполнения до 32 бит. Но его можно использовать только в реализациях, которые вообще предоставляют uint16_t. Таким образом, atomic_uint16_t можно было указать как необязательный тип. - person Peter Cordes; 13.07.2020
comment
Такая машина должна быть способна реализовать простой uint16_t без 32-битного чтения / изменения / записи, потому что отдельные потоки могут записывать смежные элементы массива неатомарного uint16_t arr[]. Таким образом, DSP с адресацией по словам и размером слова больше 16 бит не мог этого сделать. У вас может быть машина с байтовой и 16-битной чистой загрузкой и чистым хранилищем, но с возможностью атомарного RMW только для 32-битной. Я думаю, что вы это представляете, но, как я уже сказал, вы можете справиться с этим, если _Atomic uint16_t будет 4 байта. Но, может быть, никому не нужен такой сюрприз, поэтому комитет по стандартам исключил его? - person Peter Cordes; 13.07.2020
comment
И вам не нужно ничего в стандарте, что не может быть реализовано. Авторы Стандарта могут захотеть не предполагать, что одни реализации лучше других, но я бы предпочел бы, чтобы Стандарт включал то, что некоторые реализации не могут поддерживать, чем не иметь стандартного способа выполнять задачи, которые поддерживаются только 99% целевых платформ. - person supercat; 13.07.2020
comment
@supercat: К счастью, в этом случае вы можете просто написать _Atomic uint16_t в любой реализации C, которая имеет uint16_t, например в составе typedef _Atomic uint16_t atomic_u16. У этого нет недостатка в сравнении с гипотетическим atomic_uint16_t, за исключением стандартизованного имени для типа. Реализация, расширяющая его до 32-битного (например) при необходимости, обеспечивается квалификатором _Atomic, а не удобными определениями типов. Но в целом 100% согласны с тем, что C переносит, хотя и предоставляет более общие, но не универсальные функции ЦП (например, popcount и bit-scan). - person Peter Cordes; 14.07.2020
comment
@PeterCordes: К сожалению, Стандарт не допускает возможность того, что реализация может атомарно выполнять некоторые полезные операции непосредственно с собственными типами, но не выполнять с ними сравнение и замену. Если бы в Стандарте были определены необязательные функции, которые будут выполнять атомарные операции непосредственно над собственными типами и которые отделяют функции, сообщающие о различных уровнях детализации того, что они делают, это позволило бы эффективно обновлять существующие реализации, просто добавляя библиотеку, не требуя этого. компилятор знает или заботится об атомарных типах. - person supercat; 14.07.2020
comment
@PeterCordes: разделение уровней отчетности было бы важно на таких платформах, как исходный 8086 или многие микроконтроллеры, которые имеют инструкции чтения-изменения-записи, которые работают атомарно и устанавливают флаги в зависимости от того, что они сделали, но не захватывают все старые или новое значение. Даже если платформа имеет функцию сравнения и обмена, обработка декремента, например, sub dword [esi], 1 / sbc eax,eax / ret может быть намного более эффективным, чем попытка использовать цикл сравнения и замены, если код заботится о том, уменьшилось ли значение после нуля [используйте -1 в качестве значения «состояния ожидания»]. - person supercat; 14.07.2020
comment
@PeterCordes: Кроме того, во многих автономных реализациях понятие атомарных операций на основе блокировок будет в корне нарушено. Атомарная библиотека может быть довольно легко спроектирована для полезной работы, если несколько потоков попытаются работать с объектом одновременно, или если основной поток и сигнал попытаются сделать то же самое, но он должен будет знать, что первая операция может быть завершена, если второй блокирует, или что первый будет остановлен до завершения второго. Автономные реализации не смогут узнать, какой сценарий применяется. - person supercat; 14.07.2020
comment
@supercat отдельно стоящий =? - person Jason S; 15.07.2020
comment
@JasonS: размещенная реализация, как правило, предназначена для генерации кода, который выполняет ввод-вывод с использованием операционной системы, о которой знает реализация, в то время как автономная реализация предназначена для работы в контекстах, где не может быть быть операционная система, о которой знает реализация. Автономные реализации часто используются для написания кода для устройств на основе микроконтроллеров, таких как термостаты, приборы, электронные игрушки и т. Д., Которые часто не имеют операционной системы, кроме кода, предоставленного программистом. Например... - person supercat; 15.07.2020
comment
... микроконтроллер может иметь настраиваемый таймер, который заставит указанную подпрограмму выполняться 1000 раз в секунду (способом, аналогичным подаче сигнала), и программист может использовать эту подпрограмму для выполнения любых фоновых задач, которые необходимо выполнить. быть выполненным, даже если формальной операционной системы как таковой нет. - person supercat; 15.07.2020
comment
Хорошо, да, я работал над автономными реализациями почти 25 лет, просто я никогда не слышал, чтобы они так назывались. (встраиваемые системы без покрытия - это модное слово, к которому я привык) - person Jason S; 15.07.2020
comment
@supercat p.s. не могли бы вы отправить мне сообщение наедине? (проще всего через твиттер; имя пользователя указано в моем профиле) У меня вопрос. - person Jason S; 15.07.2020
comment
@JasonS: Стандарт C использует термины размещенная реализация и автономная реализация. На самом деле он не указывает никаких средств, с помощью которых автономные реализации могут делать что-либо нетривиальное, но ожидает, что такие вещи будут проблемами качества реализации, зависящими от платформы. У меня нет аккаунта в твиттере, но не стесняйтесь продолжать обсуждение в чате. - person supercat; 15.07.2020