LINUX.ORG.RU

Макрос, разворачивающийся в переменное число аргументов

 ,


0

1

Возможно ли вообще написать на Си макрос, который бы подставлялся в массив uint8_t как 1, 2, 3 или 4 числа в зависимости от аргумента? Например, если передать 0x12 то подставился как (1, 0x12), если передать 0x1234 то (2, 0x12, 0x34), если 0x123456 то (3, 0x12, 0x34, 0x56) и т.д. Пробовал даже через _Generic с явным указанием типа, но даже он выдает только одно значение.

Ответ на: комментарий от Eddy_Em

Вот в том-то и вопрос. Если бы точно знать количество аргументов, можно было бы даже лобовое решение применить

((x>>24)&0xFF), ((x>>16)&0xFF), ((x>>8)&0xFF), (x&0xFF)
Проблема в том, что если передано 1-байтное число, мне три старших байта вообще не нужны, даже нулями. То есть для 1-байтного числа массив должен получиться 2-элементным, а для 64-битного - 5-элементным.

COKPOWEHEU
() автор топика

Через шаблоны C++ скорее всего можно сделать.

X512 ★★★★★
()
Ответ на: комментарий от Eddy_Em

Вопрос, если что, про С!

Тогда никак.

/thread

X512 ★★★★★
()

Все определяется желанием:

#include <stdio.h>

// ((x>>24)&0xFF), ((x>>16)&0xFF), ((x>>8)&0xFF), (x&0xFF)
#define arr(x) {[0] = (((x)>>24)&0xFF), [(((x)>>24)&0xFF)?1:0] = (((x)>>16)&0xFF), [(((x)>>24)&0xFF)?2:(((x>>16)&0xFF)? 1 : 0)] = ((
(x)>>8)&0xFF), [(((x)>>24)&0xFF)?3:(((x>>16)&0xFF)? 2 : ( ((x)>>8)&0xFF ? 1 : 0))] = ((x)&0xFF)}
int a1[] = arr(1);
int a300[] = arr(300);
int a80000[] = arr(80000);
int a80000000[] = arr(80000000);

int main()
{
    printf("sizeof(a1) = %ld\n", sizeof(a1));
    printf("sizeof(a300) = %ld\n", sizeof(a300));
    printf("sizeof(a80000) = %ld\n", sizeof(a80000));
    printf("sizeof(a80000000) = %ld\n", sizeof(a80000000));
    return 0;
}
и копилятором:
$ gcc -o arr arr.c
$ ./arr
sizeof(a1) = 4
sizeof(a300) = 8
sizeof(a80000) = 12
sizeof(a80000000) = 16

anonymous
()
Ответ на: комментарий от anonymous

Спасибо. А можно ли это решение расширить на вставку такой последовательности в середину другого массива?

uint8_t arr[] = {1, 2, 3, arr(0x4567), 8};
Просто основная задача - написание дескрипторов для usb, а они бывают довольно заковыристыми. В частности, переменный размер аргументов характерен для HID. Вот здесь я эту задачу решил жутким костылем с хардкодом размера.

COKPOWEHEU
() автор топика
Последнее исправление: COKPOWEHEU (всего исправлений: 1)

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

xaizek ★★★★★
()
Ответ на: комментарий от COKPOWEHEU

Ничто не мешает в arr убрать фигурные скобки и прописать другие номера позиций, например, [0] превратить в [3], да и к остальным числам эту тройку добавить. … ? 1 : 0 ==> … ? 4 : 3 и далее. А 8-ка в конце автоматом попадет на последнее место.

anonymous
()
Ответ на: комментарий от anonymous

А как можно узнать номер очередного элемента массива? Там же перед ним такие же макросы будут. Не вручную же считать.

COKPOWEHEU
() автор топика
Ответ на: комментарий от COKPOWEHEU

Ерундой занимаетесь. Почему не использовать просто функциональный стиль и упаковывать обычной функцией ? Плюс то, что просите практически безкостыльно впиливается в такую функцию или серию функций под сжатый массив. Ручками тут писать легко, а отлаживать ещё легче. А вот с макросами без ног останетесь.

AKonia ★★
()
Последнее исправление: AKonia (всего исправлений: 2)
Ответ на: комментарий от AKonia

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

COKPOWEHEU
() автор топика
Ответ на: комментарий от SZT

Типа

#define MACRO(a) \
do{ \
  switch(sizeof(#a)) \
  { \
    case 5: func(1, hex_to_char(#a[2], #a[3])); break; \
    case 7: func(2, hex_to_char(#a[2], #a[3]), hex_to_char(#a[4], #a[5]));\
 итд
  } \
case 5 для «0x??» - четыре символа и нуль-терминирующий байт.

case 7 для «0x????» - шесть символов и нуль-терминирующий байт.

Из строки выковыриваем две буквы и конвертим в один байт некоей функцией hex_to_char, которую думаю ясно как написать. Если это дело все заинлайнит компилятор, проблем с оверхедом не будет.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 4)

Макрос, разворачивающийся в переменное число аргументов

Экспромтом.

Ну что такое макросы?
Грубо говоря - продвинутый шаблонизатор.

Можно PHP, Perl, Python, … использовать в качестве шаблонизатора.

PS: Не использую шаблоны совсем.

Что взамен?
Метаданные.

Владимир

anonymous
()
Ответ на: комментарий от COKPOWEHEU

А почему является проблемой записать это так, чтоб MACRO(0xAA, 0xBB); или там MACRO(0xAA, 0xBB, 0xCC) и количество аргументов через https://stackoverflow.com/a/2124385 узнавать, втуливая его первым аргументом при раскрытии макроса? Это разве сильно менее удобно?

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)

Кстати да, вполне можно макросом пришить 0x типа так:

#include <stdio.h>
#include <stdlib.h>
#define ADD0X(a) 0x##a

int main(void)
{
  printf("%x\n", ADD0X(ff));
  return EXIT_SUCCESS;
}

Ну и можно решить таким мегакостылем:


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define ADD0X(a) 0x##a

#define SPLICE(a,b) SPLICE_1(a,b)
#define SPLICE_1(a,b) SPLICE_2(a,b)
#define SPLICE_2(a,b) a##b


#define PP_ARG_N( \
          _1,  _2,  _3,  _4,  _5,  _6,  _7,  _8,  _9, _10, \
         _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
         _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
         _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
         _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
         _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
         _61, _62, _63, N, ...) N

/* Note 63 is removed */
#define PP_RSEQ_N()                                        \
         62, 61, 60,                                       \
         59, 58, 57, 56, 55, 54, 53, 52, 51, 50,           \
         49, 48, 47, 46, 45, 44, 43, 42, 41, 40,           \
         39, 38, 37, 36, 35, 34, 33, 32, 31, 30,           \
         29, 28, 27, 26, 25, 24, 23, 22, 21, 20,           \
         19, 18, 17, 16, 15, 14, 13, 12, 11, 10,           \
          9,  8,  7,  6,  5,  4,  3,  2,  1,  0

#define PP_NARG_(...)    PP_ARG_N(__VA_ARGS__)    

/* Note dummy first argument _ and ##__VA_ARGS__ instead of __VA_ARGS__ */
#define PP_NARG(...)     PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())


#define FUNC_CALL_0() \
  func(0)
#define FUNC_CALL_1(VAR) \
  func(1,ADD0X(VAR))
#define FUNC_CALL_2(VAR, VAR2) \
  func(2,ADD0X(VAR),ADD0X(VAR2))
#define FUNC_CALL_3(VAR, VAR2, VAR3) \
  func(3,ADD0X(VAR),ADD0X(VAR2),ADD0X(VAR3))
#define FUNC_CALL_4(VAR, VAR2, VAR3, VAR4) \
  func(4,ADD0X(VAR),ADD0X(VAR2),ADD0X(VAR3),ADD0X(VAR4))
// И так далее


#define FUNC_CALL_(N, ...) \
  SPLICE(FUNC_CALL_, N)(__VA_ARGS__)

#define FUNC_CALL(...) \
  FUNC_CALL_(PP_NARG(__VA_ARGS__), __VA_ARGS__)



void func(int n, ...)
{
  va_list list;
  va_start(list, n);
  printf("{");
  for (int i=0; i < n; i++)
  {
    printf("0x%x,", va_arg(list, int));
  }
  printf("}");
  va_end(list);
}

int main(void)
{
  FUNC_CALL(ff,aa,bb,cc);
  return EXIT_SUCCESS;
}

SZT ★★★★★
()
Ответ на: комментарий от fernandos

И что с того? Все равно на до-диезе только безмозглые вантузоиды пишут. Аналогично со всякими goвнами и педеrustами.

Линуксоиды выбирают С или С++.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от fernandos

При том что исходная тема про макросы для контроллера и соответственно минимальный оверхед и достаточно читаемый код.

COKPOWEHEU
() автор топика
Ответ на: комментарий от SZT

А почему является проблемой записать это так, чтоб MACRO(0xAA, 0xBB);

Сейчас оно выглядит как-то так:

uint8_t descriptor[] = {
...
USAGE( USAGE_X ), // 0x09, 0x30,
USAGE( USAGE_Y ), // 0x09, 0x31,
LOGICAL_MINMAX16( 0, 10000 ), //0x16, 0x00, 0x00, 0x26, 0x10, 0x27,
REPORT_FMT( 16, 2 ), // 0x75, 0x10, 0x95, 0x02,
...
};

COKPOWEHEU
() автор топика
Ответ на: комментарий от COKPOWEHEU

Если надо с десятичным, проще уж будет взять внешний препроцессор...

Не, можно конечно сделать лютые костыли на препроцессоре, когда генерим кучу макросов вида

#define NUMSPLIT_0 1, 0x00
#define NUMSPLIT_1 1, 0x01
#define NUMSPLIT_2 1, 0x02
// ...
#define NUMSPLIT_255 1, 0xff
#define NUMSPLIT_256 2, 0x01, 0x00
#define NUMSPLIT_257 2, 0x01, 0x01
#define NUMSPLIT_258 2, 0x01, 0x02
// ...
#define NUMSPLIT_65535 2, 0xff, 0xff
#define NUMSPLIT_65536 3, 0x01, 0x00, 0x01
#define NUMSPLIT_65537 3, 0x01, 0x00, 0x02
// ...

#define FUNC_CALL2(x) func(NUMSPLIT_##x)

Еще можно

#define NUMSPLIT_0x0  1, 0x00
#define NUMSPLIT_0x00 1, 0x00
#define NUMSPLIT_0x1 1, 0x01
#define NUMSPLIT_0x01 1, 0x01
// ...
Но может ну его нафиг?

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: комментарий от SZT

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

AKonia ★★
()
Ответ на: комментарий от Eddy_Em

Аналогично со всякими goвнами и педеrustами.

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

anonymous
()
Ответ на: комментарий от AKonia

Во-первых это требует немало памяти

Внешний препроцессор цеплять надо, на встроенном сишном сильно далеко не уедешь.

SZT ★★★★★
()
Ответ на: комментарий от Eddy_Em

Фу, гомофоб …

Линуксоиды выбирают С или С++.

Я линуксоид, но по роду работы - 1С-ник, а в душе - перловщик …

Владимир

anonymous
()
Ответ на: комментарий от COKPOWEHEU

Проблема в том, что если передано 1-байтное число, мне три старших байта вообще не нужны, даже нулями.

Проверяй цепочкой условий if(x < 0xff) {func(1, x);} else if(x < 0xffff) {func (2, (x>>8)&0xFF, x&0xff);} else if(... внутри макроса. Компилятор проверку должен выкинуть, если на этапе компиляции ему известно конкретное число.

SZT ★★★★★
()
Ответ на: комментарий от Eddy_Em

Я самый известный и уважаемый аноним на форуме. К моему мнению прислушиваются даже модераторы …

Владимир

anonymous
()
Ответ на: комментарий от AKonia

Во-первых это требует немало памяти, во-вторых не совсем понятно почему автор не хочет просто функцию написать которая будет выдавать только ненулевые байты

Сильно усложнится основной код. По сути нужно будет писать свой язык для описания дескриптора. Уж лучше тот костыль, что сейчас.

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

Каким способом? Те 1-4 байта это малая часть дескриптора.

нет хочется странного - повозится с макросами.

Так а какая альтернатива? Городить внешние костыли? Переходить на другой язык программирования?

COKPOWEHEU
() автор топика
Ответ на: комментарий от SZT

Проверяй цепочкой условий if(x < 0xff) {func(1, x);} else if(x < 0xffff) {func (2, (x>>8)&0xFF, x&0xff);} else if(… внутри макроса. Компилятор проверку должен выкинуть, если на этапе компиляции ему известно конкретное число.

Такое нельзя встроить в инициализацю массива. Уж количество элементов должно быть известно на начало компиляции

COKPOWEHEU
() автор топика
Ответ на: комментарий от COKPOWEHEU

Тогда дефайны генерируй, вот генератор дефайнов для десятичных чисел https://wandbox.org/permlink/gTo1cLLRMm0Z9C7U и потом через #define SPLIT(x) NUMSPLIT_##x оно развернется

Или бери внешний препроцессор, например есть https://nedbatchelder.com/code/cog/index_ru.html

SZT ★★★★★
()
Ответ на: комментарий от SZT

вот генератор дефайнов для десятичных чисел https://wandbox.org/permlink/gTo1cLLRMm0Z9C7U

Правда там файл получится в несколько десятков гигов, так что это конечно бред полный. Можно что-то попробовать придумать через рекурсивные инклуды, но лучше не стоит.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Ответ на: комментарий от SZT

Можно что-то попробовать придумать через рекурсивные инклуды

Тоже думал об этом, но это ведь те же макросы, то есть переменную из Си туда не протолкнуть.

Да и гигабайты временных файлов чтобы собрать прошивку в 3 кБ тоже какой-то бред.

Использование стороннего препроцессора в данном случае не лучше написание своего конфигуратора. Можно, конечно, но оно того не стоит.

COKPOWEHEU
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.