What does ## (double hash) do in a preprocessor directive?

TLDR; ## is for concatenation and # is for stringification (from cppreference).

The ## concatenates successive identifiers and it is useful when you want to pass a function as a parameter. Here is an example where foo accepts a function argument as its 1st argument and the operators a and b as the 2nd and 3rd arguments:

#include <stdio.h>
enum {my_sum=1, my_minus=2};
#define foo(which, a, b) which##x(a, b)
#define my_sumx(a, b) (a+b)
#define my_minusx(a, b) (a-b)

int main(int argc, char **argv) {
    int a = 2;
    int b = 3;
    printf("%d+%d=%d\n", a, b,  foo(my_sum, a, b));  // 2+3=5
    printf("%d-%d=%d\n", a, b, foo(my_minus, a, b)); // 2-3=-1
    return 0;
}

The # concatenates the parameter and encloses the output in quotes. The example is:

#include <stdio.h> 
#define bar(...) puts(#__VA_ARGS__)
int main(int argc, char **argv) {
    bar(1, "x", int); // 1, "x", int
    return 0;
}

## is the preprocessor operator for concatenation.

So if you use

DEFINE_STAT(foo)

anywhere in the code, it gets replaced with

struct FThreadSafeStaticStat<FStat_foo> StatPtr_foo;

before your code is compiled.

Here is another example from a blog post of mine to explain this further.

#include <stdio.h>

#define decode(s,t,u,m,p,e,d) m ## s ## u ## t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
    printf("Stumped?\n");
}

This program would compile and execute successfully, and produce the following output:

Stumped?

When the preprocessor is invoked on this code,

  • begin is replaced with decode(a,n,i,m,a,t,e)
  • decode(a,n,i,m,a,t,e) is replaced with m ## a ## i ## n
  • m ## a ## i ## n is replaced with main

Thus effectively, begin() is replaced with main().