макрос с тернарным оператором - могу ли я пройти через переменную во время выполнения?

Мне было интересно, могу ли я передать нестатическую переменную через макрос препроцессора. Представьте, что мне нужно вызвать функцию, которая принимает 4 аргумента, например foo(A,B,C,D); и мой ввод - bar, который представляет собой целочисленное значение от 1 до 4, и baz, которое может быть любым целым значением во время выполнения, которое входит в A, если 'bar' == 1, B, если оно' == 2... D, если оно = = 4. Я придумал макрос, который выглядит примерно так:

#define setACports(p,v) ((p) == 4 ? ", , ,(v),"  : \
                        ((p) == 3 ? ", ,(v), ,"  : \
                        ((p) == 2 ? ",(v), , ,"  : \
                       (((p) == 1 ? "(v), , , ," : "invalid")))))

/*example application */
void foo(int var1,int var2,int var3, int var4);                           
int main (void) {
    int i = 0;
    for (i=0;i<5;i++) 
        foo(setACports(i,i));
}

Теперь, хотя аргумент «позиционирование» работает нормально, значение, очевидно, работает неправильно, потому что (v) недоступен во время препроцессора. Как мне реализовать что-то подобное лучше всего? Для меня загадка, что может быть лучшим решением, любые предложения или подсказки были бы замечательными!


person stdcerr    schedule 20.07.2019    source источник
comment
На ум приходит простая функция, принимающая массив достаточного размера (вместе с p, v) и использующая sprintf для заполнения. В противном случае выделите строку внутри самой функции и верните указатель на нее. Есть ли известное ограничение на то, как долго может быть v?   -  person David C. Rankin    schedule 20.07.2019
comment
@DavidC.Rankin Надеюсь, теперь я разъяснил свой пример выше (избавился от printf), да, диапазон p известен и равен 1-4, v - 8-битное значение, 0 - 0xff   -  person stdcerr    schedule 20.07.2019


Ответы (2)


Вы могли бы сделать следующее:

#include <stdio.h>

#define setACports(p, v) \
  (p) == 1 ?(v) :0, \
  (p) == 2 ?(v) :0, \
  (p) == 3 ?(v) :0, \
  (p) == 4 ?(v) :0

void foo(int var1, int var2, int var3, int var4)
{
  printf("var1 = %d, var2 = %d, var3 = %d, var4 = %d\n", 
    var1, var2, var3, var4);
}

int main(void) 
{
  for (int i = 0; i < 5; ++i) 
  {
    printf("i = %d: ", i);
    foo(setACports(i, i));
  }
}

Это выводит:

i = 0: var1 = 0, var2 = 0, var3 = 0, var4 = 0
i = 1: var1 = 1, var2 = 0, var3 = 0, var4 = 0
i = 2: var1 = 0, var2 = 2, var3 = 0, var4 = 0
i = 3: var1 = 0, var2 = 0, var3 = 3, var4 = 0
i = 4: var1 = 0, var2 = 0, var3 = 0, var4 = 4

Чтобы определить случай p<1 || p>4 внутри макроса, вы можете изменить одну из тернарных операций, чтобы она выглядела следующим образом (первая в данном случае):

  (p) == 1 \
    ?(v) \
    :((p) < 1 || (p) > 4 \
      ?fflush(stdout), fprintf(stderr, "Invalid: %d", v), exit(EXIT_FAILURE), -1 \
      :0), \

Результат тогда выглядел так:

i = 0: Invalid: 0

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

Чтобы иметь возможность продолжать работу, можно можно изменить макрос, чтобы фактически выполнять вызов функции:

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

#define setACports2(f, p, v) ( \
  ((p) < 1 || (p) > 4) \
    ? fflush(stdout), errno = ERANGE, perror(#f "()"), -1 \
    : ( \
      f( \
        (p) == 1 ?(v) :0, \
        (p) == 2 ?(v) :0, \
        (p) == 3 ?(v) :0, \
        (p) == 4 ?(v) :0 \
      ), 0 \
    ) \
  )

void foo(int var1, int var2, int var3, int var4)
{
  printf("var1 = %d, var2 = %d, var3 = %d, var4 = %d\n",
    var1, var2, var3, var4);
}

int main(void)
{
  for (int i = 0; i < 5; ++i)
  {
    int vi = i;
    printf("i = %d: ", i);
    if (-1 == setACports2(foo, i, vi))
    {
      fprintf(stderr, "setACports() failed\n");
    }
  }
}

Это выведет:

i = 0: foo(): Numerical result out of range
setACports() failed
i = 1: var1 = 1, var2 = 0, var3 = 0, var4 = 0
i = 2: var1 = 0, var2 = 2, var3 = 0, var4 = 0
i = 3: var1 = 0, var2 = 0, var3 = 3, var4 = 0
i = 4: var1 = 0, var2 = 0, var3 = 0, var4 = 4

Эта линия

    if (-1 == setACports2(foo, i, vi))

расширится до:

    if (-1 == ( ((i) < 1 || (i) > 4) ? fflush(stdout), errno = ERANGE, perror("foo" "()"), -1 : ( foo((i) == 1 ?(vi) :0, (i) == 2 ?(vi) :0, (i) == 3 ?(vi) :0, (i) == 4 ?(vi) :0), 0 ) ))

Поскольку setACports2 дублирует setACports, это может также выглядеть так:

#define setACports2(f, p, v) ( \
  ((p) < 1 || (p) > 4) \
    ? fflush(stdout), errno = ERANGE, perror(#f "()"), -1 \
    : ( \
      f(setACports(p, v)), 0 \
    ) \
  )

Дальнейшие чтения, связанные с этим ответом:

person alk    schedule 20.07.2019

В этом конкретном примере, поскольку вы печатаете, вы можете заменить v на %d и добавить параметр в конец.

#define setACports(p,v) ((p) == 4 ? ", , ,(%d),"  : \
                        ((p) == 3 ? ", ,(%d), ,"  : \
                        ((p) == 2 ? ",(%d), , ,"  : \
                       (((p) == 1 ? "(%d), , , ," : "invalid"))))), (v)

Вывод:

invalid(1), , , ,,(2), , ,, ,(3), ,, , ,(4),
person dbush    schedule 20.07.2019
comment
но это только потому, что я печатаю, имейте в виду, что настоящие вещи будут списком аргументов для другой функции... возможно, мне следует соответствующим образом переписать исходный пост... - person stdcerr; 20.07.2019