CodeWalk

宏定义中的#与##运算符:字符串化和连接

作者:古法程序员 · 2026-05-30 12:55

请说明 C/C++ 宏中 #(字符串化)和 ##(标记连接)运算符的作用和用法,给出它们在日志系统、注册表、X-Macro 等场景中的实际应用示例。

回答

古法程序员

# 字符串化:将宏参数转为字符串字面量。

#define STR(x)   #x
#define TO_STR(x) STR(x)  // 确保参数先展开

int version = 42;
cout << STR(hello);        // "hello"
cout << TO_STR(__LINE__);  // "42"(先展开 __LINE__)

// 日志宏
#define LOG(msg) cout << __FILE__ ":" << TO_STR(__LINE__) << " " << msg << '\n';

## 标记连接:将两个标记拼接为一个新标记。

#define MAKE_TYPE(name) using name##Vector = std::vector<name>
MAKE_TYPE(int);    // using intVector = std::vector<int>
MAKE_TYPE(string); // using stringVector = std::vector<string>

// 字段访问器
#define FIELD(name)                   \
    decltype(auto) get_##name() { return name##_; } \
    void set_##name(const auto& v) { name##_ = v; }

class Person {
    int age_;
    string name_;
public:
    FIELD(age)
    FIELD(name)
};  // 生成 get_age()/set_age()/get_name()/set_name()

X-Macro 中的 ## 应用

#define ERROR_MAP \
    X(OK,          0, "Success")    \
    X(NOT_FOUND,   1, "Not found")

enum class ErrorCode {
#define X(name, code, msg) name = code,
    ERROR_MAP
#undef X
};

#define ERROR_TO_STRING_CASE(name, code, msg) \
    case ErrorCode::name: return msg;

const char* to_string(ErrorCode e) {
    switch(e) {
        ERROR_MAP
#undef X
        default: return "Unknown";
    }
}

注意:## 生成的标记必须是合法的预处理标记。参数为 __VA_ARGS__ 时 ## 可用于去除多余逗号。