# 和 ## 的妙用

282 阅读2分钟

「这是我参与11月更文挑战的第 2 天,活动详情查看:2021最后一次更文挑战」。

示例一

之前在这篇文章中【转载】RAII 妙用之 ScopeExit 涉及过 ## 拼接功能的使用

代码如下:

 /// @note 通过一系列宏定义构造 ScopeExit 类的实例
 #define _CONCAT(a, b) a##b
 #define _MAKE_SCOPE_(line) ScopeExit _CONCAT(defer, line) = [&]()
 
 #undef SCOPE_GUARD
 #define SCOPE_GUARD _MAKE_SCOPE_(__LINE__)

利用宏定义中将变量名和行号拼接起来,实现了根据宏定义所在行号命名 ScopedExit 对象(防止重名)的功能

image.png

示例二

该示例的功能主要是将满足一定命名规范的函数封装为一个 Command 结构体数组

Command 结构体定义如下

/// <summary>
/// 封装了函数名和函数指针的 command 结构体
/// </summary>
struct command {
    const char* name;
    void (*function) (void); 
};

宏定义代码如下,除了利用了之前提到的 ## 的拼接功能外(假设封装的每个函数名后缀都为 _command),还有 # 的转字符串的功能

/// @note NAME 需要和以下实体函数的名字相符合
#define COMMAND(NAME) {#NAME, NAME##_command}

封装部分的代码为

    /// <summary>
    /// 封装之后,方便遍历和随机访问 commands 
    /// </summary>
    /// <returns></returns>
    struct command commands[] = {
        COMMAND(quit),
        COMMAND(help),
    };

完整示例代码如下

/// #: 将后面的 宏参数 转为字符串,即 将后面的参数用双引号引起来
/// ##:就是用于拼接

/// @note NAME 需要和以下实体函数的名字相符合
#define COMMAND(NAME) {#NAME, NAME##_command}

/// <summary>
///  用于初始化 command 中函数指针的实体函数
/// 名字满足上面宏定义的 NAME_command 的模式
/// </summary>
void quit_command() {
    printf("I am quit command\n");
}

/// <summary>
/// 同上
/// </summary>
void help_command() {
    printf("I am help command\n");
}

/// <summary>
/// 封装了函数名和函数指针的 command 结构体
/// </summary>
struct command {
    const char* name;
    void (*function) (void);
};

int main() {

    /// <summary>
    /// 封装之后,方便遍历和随机访问 commands 
    /// </summary>
    /// <returns></returns>
    struct command commands[] = {
        COMMAND(quit),
        COMMAND(help),
    };

    commands[0].function();
    commands[1].function();

    return 0;
}

参考文章

宏定义中 # 和 ## 的作用 该文章代码有小错误,我进行了修正