C macro containing static variables as conditional

1.1k Views Asked by At

I am wondering if it is possible in plain C (C99) to have a macro which contains static variables which can be used inside conditional expressions.

I came up with the following (as a function):

char EDGE(unsigned long x)
{
    static char up = 0;
    static unsigned long v;
    if (up && v != x) {
        v = x;
        return 1;
    } else {
        v = x;
        up = 1;
        return 0;
    }
}

which is exactly what I want, except that it only works once (one misses the cached value on invoking this function with another variable).

So I thought about macros in C and the fact, that if this would work I got a variable cache for free, without having to manage anything. Is this even possible using plain old C?

Note: I want to have a simple to use edge detection 'function' which might be simply invoked inside an if-statement.

1

There are 1 best solutions below

4
dbush On BEST ANSWER

For this, you'll need two macros. The first is used to set up an edge function for a particular variable, while the second is used to call the relevant function.

#include <stdio.h>

#define DEF_EDGE(var) static char edge##var(unsigned long var) \
{ \
    static char up = 0; \
    static unsigned long v = 0; \
    if (up && v != var) { \
        v = var; \
        return 1; \
    } else { \
        v = var; \
        up = 1; \
        return 0; \
    } \
}

#define EDGE(var) edge##var(var)

DEF_EDGE(x)
DEF_EDGE(y)

int main()
{
    unsigned long x = 2, y = 3;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    x=3;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    y=4;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    x=1; y=2;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    return 0;
}

Output:

x=2, y=3 edge(x)=0, edge(y)=0
x=2, y=3 edge(x)=0, edge(y)=0
x=3, y=3 edge(x)=1, edge(y)=0
x=3, y=3 edge(x)=0, edge(y)=0
x=3, y=4 edge(x)=0, edge(y)=1
x=3, y=4 edge(x)=0, edge(y)=0
x=1, y=2 edge(x)=1, edge(y)=1
x=1, y=2 edge(x)=0, edge(y)=0

In each of these macros, ## is the concatenation operator. This combines two tokens into one.

When you use the first macro at file scope as:

DEF_EDGE(y)

This expands into:

static char edgey(unsigned long var)
{
    static char up = 0;
    static unsigned long v = 0;
    if (up && v != var) {
        v = var;
        return 1;
    } else {
        v = var;
        up = 1;
        return 0;
    }
}

Now you have a function that can check for an edge condition for y.

Then when you use the second macro:

EDGE(y)

This expands to:

edgey(y)

Note that for this to work, each variable you set up an EDGE function for has to have a unique name, regardless of scope.

EDIT:

Here's another way to do this that doesn't have scoping issues:

#include <stdio.h>

static char edge(unsigned long var, unsigned long *v)
{
    if (*v != var) {
        *v = var;
        return 1;
    } else {
        *v = var;
        return 0;
    }
}

#define EDGE(var) edge(var, &var##_last)

#define SET_EDGE(var) \
    unsigned long var##_last = var;

int main()
{
    unsigned long x = 2, y = 3;
    SET_EDGE(x);
    SET_EDGE(y);

    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    x=3;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    y=4;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    x=1; y=2;
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    printf("x=%lu, y=%lu edge(x)=%d, edge(y)=%d\n", x, y, EDGE(x), EDGE(y));
    return 0;
}

In this case, you call the SET_EDGE macro just after the relevant variable is defined. This creates another variable with _last appended to the name, keeping track of the prior value. The address of this state variable is then passed to the edge function when you use the EDGE mecro.