What are the applications of the ## preprocessor o

2018-12-31 15:20发布

As mentioned in many of my previous questions, I'm working through K&R, and am currently into the preprocessor. One of the more interesting things — something I never knew before from any of my prior attempts to learn C — is the ## preprocessor operator. According to K&R:

The preprocessor operator ## provides a way to concatenate actual arguments during macro expansion. If a parameter in the replacement text is adjacent to a ##, the parameter is replaced by the actual argument, the ## and surrounding white space are removed, and the result is re-scanned. For example, the macro paste concatenates its two arguments:

#define paste(front, back) front ## back

so paste(name, 1) creates the token name1.

How and why would someone use this in the real world? What are practical examples of its use, and are there gotchas to consider?

13条回答
唯独是你
2楼-- · 2018-12-31 15:35

You can use token pasting when you need to concatenate macro parameters with something else.

It can be used for templates:

#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};

In this case LINKED_LIST(int) would give you

struct list_int {
int value;
struct list_int *next;
};

Similarly you can write a function template for list traversal.

查看更多
无与为乐者.
3楼-- · 2018-12-31 15:41

It is very useful for logging. You can do:

#define LOG(msg) log_msg(__function__, ## msg)

Or, if your compiler doesn't support function and func:

#define LOG(msg) log_msg(__file__, __line__, ## msg)

The above "functions" logs message and shows exactly which function logged a message.

My C++ syntax might be not quite correct.

查看更多
浅入江南
4楼-- · 2018-12-31 15:42

SGlib uses ## to basically fudge templates in C. Because there's no function overloading, ## is used to glue the type name into the names of the generated functions. If I had a list type called list_t, then I would get functions named like sglib_list_t_concat, and so on.

查看更多
时光乱了年华
5楼-- · 2018-12-31 15:43

A previous question on Stack Overflow asked for a smooth method of generating string representations for enumeration constants without a lot of error-prone retyping.

Link

My answer to that question showed how applying little preprocessor magic lets you define your enumeration like this (for example) ...;

ENUM_BEGIN( Color )
  ENUM(RED),
  ENUM(GREEN),
  ENUM(BLUE)
ENUM_END( Color )

... With the benefit that the macro expansion not only defines the enumeration (in a .h file), it also defines a matching array of strings (in a .c file);

const char *ColorStringTable[] =
{
  "RED",
  "GREEN",
  "BLUE"
};

The name of the string table comes from pasting the macro parameter (i.e. Color) to StringTable using the ## operator. Applications (tricks?) like this are where the # and ## operators are invaluable.

查看更多
旧人旧事旧时光
6楼-- · 2018-12-31 15:44

One important use in WinCE:

#define BITFMASK(bit_position) (((1U << (bit_position ## _WIDTH)) - 1) << (bit_position ## _LEFTSHIFT))

While defining register bit description we do following:

#define ADDR_LEFTSHIFT                          0

#define ADDR_WIDTH                              7

And while using BITFMASK, simply use:

BITFMASK(ADDR)
查看更多
余欢
7楼-- · 2018-12-31 15:47

CrashRpt: Using ## to convert macro multi-byte strings to Unicode

An interesting usage in CrashRpt (crash reporting library) is the following:

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.

Here they want to use a two-byte string instead of a one-byte-per-char string. This probably looks like it is really pointless, but they do it for a good reason.

 std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

They use it with another macro that returns a string with the date and time.

Putting L next to a __ DATE __ would give you a compiling error.


Windows: Using ## for generic Unicode or multi-byte strings

Windows uses something like the following:

#ifdef  _UNICODE
    #define _T(x)      L ## x
#else
    #define _T(x) x
#endif

And _T is used everywhere in code


Various libraries, using for clean accessor and modifier names:

I've also seen it used in code to define accessors and modifiers:

#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)

Likewise you can use this same method for any other types of clever name creation.


Various libraries, using it to make several variable declarations at once:

#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;
查看更多
登录 后发表回答