Предполагая малое кодирование endian.
Чтобы преобразовать в UInt16 из [UInt8], вы можете сделать что-то вроде
var x: [UInt8] = [0x01, 0x02]
var y: UInt16 = 0
y += UInt16(x[1]) << 0o10
y += UInt16(x[0]) << 0o00
. Для преобразования в UInt32 этот шаблон распространяется на
var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
var y: UInt32 = 0
y += UInt32(x[3]) << 0o30
y += UInt32(x[2]) << 0o20
y += UInt32(x[1]) << 0o10
y += UInt32(x[0]) << 0o00
Восьмеричное представление величины сдвига дает хорошую индикацию о том, сколько полных байтов смещено (8 становится 0o10, 16 становится 0o20 и т. д.).
Это можно свести к следующему для UInt16:
var x: [UInt8] = [0x01, 0x02]
let y: UInt16 = reverse(x).reduce(UInt16(0)) {
$0 << 0o10 + UInt16($1)
}
и для UInt32:
var x: [UInt8] = [0x01, 0x02, 0x03, 0x04]
let y: UInt32 = reverse(x).reduce(UInt32(0)) {
$0 << 0o10 + UInt32($1)
}
Сокращенная версия также работает для UInt64, а также обрабатывает значения, в которых байтовая кодировка не использует все байты, такие как [0x01, 0x02, 0x03]
#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)
int NAME(some_function)(int a);
Честно говоря, вы не хотите знать, почему это работает. Если вы знаете, почему это работает, вы станете тем парнем на работе, который разбирается в таких вещах, и все будут приходить и задавать вам вопросы. =)
Редактировать: если вы действительно хотите знать, почему это работает, я с радостью опубликую объяснение, предполагая, что меня никто не опередит.
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
В комментарии к другому ответу Кейд Ру спросил , почему для этого нужны два уровня косвенного обращения. Легкомысленный ответ заключается в том, что стандарт требует, чтобы это работало; вы, как правило, обнаруживаете, что вам также нужен эквивалентный трюк со строковым оператором.
Раздел 6.10.3 стандарта C99 охватывает «замену макросов», а 6.10.3.1 - «подстановку аргументов».
были идентифицированы вызовы макроса, подобного функции, имеет место подстановка аргументов. Параметр в списке замены, если не указано иное. с помощью токена предварительной обработки
#
или##
или с последующим маркером предварительной обработки##
(см. ниже), является заменяется соответствующим аргументом после того, как все содержащиеся в нем макросы были расширен. Перед заменой токены предварительной обработки каждого аргумента полностью заменены макросы, как если бы они образовали остальную часть препроцессорного файла; нет другого доступны токены предварительной обработки.
В вызове NAME (my)
аргумент - «мой»; он полностью расширен до «моего»; затем он подставляется в заменяющую строку:
EVALUATOR(mine, VARIABLE)
Теперь макрос EVALUATOR обнаружен, и аргументы изолированы как «мои» и «ПЕРЕМЕННАЯ»; последний затем полностью расширяется до '3' и подставляется в заменяющую строку:
PASTER(mine, 3)
Операция этого регулируется другими правилами (6.10.3.3 'Оператор ##'):
Если в замене список макроса, подобного функции, перед параметром сразу ставится или за которым следует токен предварительной обработки
##
, параметр заменяется соответствующим последовательность токенов предварительной обработки аргумента; [...]Для вызовов макросов как объектных, так и функциональных, до того, как список замены будет пересмотрены на предмет замены других имен макросов, каждый экземпляр токена предварительной обработки
##
в списке замены (не из аргумента) удаляется и предыдущая предварительная обработка токен объединяется со следующим токеном предварительной обработки.
Итак, список замены содержит x
, за которым следует ##
, а также ##
, за которым следует y
; Итак, у нас есть:
mine ## _ ## 3
и удаление токенов ##
и конкатенация токенов с обеих сторон объединяет «мой» с «_» и «3», чтобы получить:
mine_3
Это желаемый результат.
Если мы посмотрим на исходный вопрос, код был (адаптирован для использования «мой» вместо «some_function»):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
Аргумент NAME явно «мой», и он полностью раскрыт.
Следуя правилам 6.10.3.3, мы находим:
mine ## _ ## VARIABLE
который, когда операторы ##
удаляются, отображается в:
mine_VARIABLE
точно так, как указано в вопросе.
Есть ли способ сделать это с помощью традиционного препроцессора C, который не имеет оператора вставки токена
##
?
Может быть, и может и нет - это зависит от препроцессора. Одним из преимуществ стандартного препроцессора является то, что он имеет надежную функцию, в то время как существовали различные реализации для предварительных стандартных препроцессоров. Одно из требований заключается в том, что когда препроцессор заменяет комментарий, он не генерирует пробел, как это требуется препроцессору ANSI. Препроцессор GCC (6.3.0) C удовлетворяет этому требованию; препроцессор Clang из XCode 8.2.1 этого не делает.
Когда это работает, это выполняет свою работу ( x-paste.c
):
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
Обратите внимание, что между fun,
и VARIABLE нет пробела
- это важно, потому что, если оно присутствует, оно копируется в вывод, и в итоге вы получаете mine_ 3
в качестве имени, что, конечно, синтаксически неверно. (А теперь, пожалуйста, можно мне вернуть волосы назад?)
С GCC 6.3.0 (запущенным cpp -traditional x-paste.c
) я получаю:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
С Clang из XCode 8.2. 1, я получаю:
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
Эти пробелы все портят. Замечу, что оба препроцессора правильные; разные предварительные стандартные препроцессоры демонстрировали оба поведения, что делало вставку токена чрезвычайно раздражающим и ненадежным процессом при попытке переноса кода. Стандарт с обозначением ##
радикально упрощает это.
Могут быть другие способы сделать это. Однако это не работает:
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC генерирует:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
Close, но без кубиков. YMMV, конечно, в зависимости от используемого вами препроцессора предварительного стандарта. Откровенно говоря, если вы застряли с препроцессором, который не взаимодействует, вероятно, было бы проще организовать использование стандартного препроцессора C вместо предстандартного (обычно есть способ настроить компилятор соответствующим образом), чем использовать тратить много времени, пытаясь найти способ выполнить эту работу.