0

I was wondering if I can funnel a non-static variable through a preprocessor macro. Imagine I have to call a function that takes 4 arguments like foo(A,B,C,D); and my input is bar which is an integer value between 1 and 4 and baz which could by any integer value at runtime which goes into A if 'bar' == 1, B if it' == 2 ... D if it's == 4. I came up with a macro that looks something like:

#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));
}

Now, while the argument "positioning" work fine, the value obviously does not work correctly because (v) is not available at pre-processor time. How would I implement something like this best? It's a puzzle to me what might be the best solution, any suggestions or hints would be great!

alk
  • 69,737
  • 10
  • 105
  • 255
stdcerr
  • 13,725
  • 25
  • 71
  • 128
  • A simple function taking either an array of sufficient size (along with `p, v`) and using `sprintf` to populate comes to mind. Otherwise, allocate for the string within the function itself and return a pointer to it. Is there a known limit on how long `v` can be? – David C. Rankin Jul 20 '19 at 03:10
  • @DavidC.Rankin I hope I clarified my example above now (got rid of the printf),Yes, the range of `p` is known and is `1-4`, `v` is an 8bit value, `0 - 0xff` – stdcerr Jul 20 '19 at 03:40

2 Answers2

1

You could do:

#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));
  }
}

This outputs:

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

To detect the case of p<1 || p>4 inside the macro you could modify one of the ternary-operations to look like this (the 1st one in this case):

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

The output then looked like this:

i = 0: Invalid: 0

Please note that the macro cannot skip the function call, but only either call the function or end the program, as the example above does.

To be able to continue running one could modify the macro to actually do the function call:

#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");
    }
  }
}

This would output:

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

This line

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

would expand to:

    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 ) ))

As setACports2 duplicates setACports it could also look like this:

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

Further readings related to this answer:

alk
  • 69,737
  • 10
  • 105
  • 255
-1

For this particular example, since you're printing you can replace v with %d and append the parameter to the end.

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

Output:

invalid(1), , , ,,(2), , ,, ,(3), ,, , ,(4),
dbush
  • 205,898
  • 23
  • 218
  • 273
  • but that's just because I'm printing, mind the real things is going to be an argument list for a different function... maybe I should rewrite my original post accordingly... – stdcerr Jul 20 '19 at 03:35