获取C语言的宏对应的字符串

探讨如何获取一个宏对应的字符串,或整数值对应的宏名称字符串。最后提供了一个轻量级的helper类实现这种机制。

问题

假设定义了下面这些宏:

#define ZERO 0
#define ONE 1
#define TWO 2

代码中在打印日志的时候,希望把这些整数(0、1、2)对应的宏的名称打印出来,即”ZERO”, “ONE”, “TWO”这种字符串。

实现a

可以借助预处理的#语法:

//M2S: Macro to String
#define M2S(x) #x

使用示例:

void foo(int v)
{
    switch (v) {
    case ZERO:
        std::cout<< M2S(ZERO) <<std::endl;
        break;
    case ONE:
        std::cout<< M2S(ONE) <<std::endl;
        break;
    case TWO:
        std::cout<< M2S(TWO) <<std::endl;
        break;
    default:
        std::cout<<"Unknown"<<std::endl;
    }
}

int main()
{
    for (int i = 0; i < MAX + 1; i++) {
        foo(i);
    }

    return 0;
}

运行结果:

ZERO
ONE
TWO
Unknown

注:因为#的行为是属于预编译的一部分,所以下面的做法是行不通的:

std::cout<< #ZERO << std::endl;

问题

在foo中的std::cout调用可否消除重复,只用一行呢?即:

void foo(int v)
{
    if (v >= ZERO && v <= TWO) {
        std::cout<< M2S(v) <<std::endl;
    } else {
        std::cout<<"Unknown"<<std::endl;
    }
}

此时运行结果:

v
v
v
Unknown

显然,这并不是我们想要的结果。为此,可以借助下面的一种方法。

实现b

// see: http://gcc.gnu.org/onlinedocs/cpp/Stringification.html

#include <iostream>
#include <map>

#define ZERO 0
#define ONE 1
#define TWO 2
#define MAX 3

//M2S: Macro to String
#define M2S(x) #x

struct Bar {
    int i;
    const char* name;
};

//VNP: Value-Name Pair
#define VNP(x) {x, #x}

struct Bar values[] = {
    VNP(ZERO),
    VNP(ONE),
    VNP(TWO)
};

const char* GetName(int i) 
{
    if (i >= 0 && i < MAX) return values[i].name;

    return "Unknown";
};

int main()
{
    for (int i = 0; i < MAX + 1; i++) {
        std::cout<<i<<": "<<GetName(i)<<std::endl;
    }

    return 0;
}

运行结果:

0: ZERO
1: ONE
2: TWO
3: Unknown
请按任意键继续. . .

模块化

因为以上的使用场景是常见的,且一个项目中可能为多个宏定义组做这个事情,因此考虑封装。

代码

新建一个(比如)MacroName的class。

头文件:

#ifndef __MACRO_NAME_H
#define __MACRO_NAME_H

#include <map>

//VNP: Value-Name Pair
#define VNP(x) x, #x

extern const char* UNKNOWN;
class MacroName {
public:
    void Add(int v, const char* name);
    const char* GetName(int v) const;

private:
    typedef std::map<int, const char*> VNP_Type;
    VNP_Type m_values;
};

#endif

实现文件:

#include "MacroName.h"

const char* UNKNOWN = "Unknown";

void MacroName::Add(int v, const char* name)
{
    m_values.insert(std::make_pair(v, name));
}

const char* MacroName::GetName(int v) const
{
    VNP_Type::const_iterator i = m_values.find(v);
    if (i != m_values.end()) return i->second;

    return UNKNOWN;
}

测试文件:

// see: http://gcc.gnu.org/onlinedocs/cpp/Stringification.html

#include <iostream>
#include "MacroName.h"

#define ZERO 0
#define ONE 1
#define TWO 2
#define MAX 3

//M2S: Macro to String
#define M2S(x) #x

void init(MacroName &mn) 
{
    mn.Add(VNP(ZERO)); //mn.Add(ZERO, M2S(ZERO));
    mn.Add(VNP(ONE));
    mn.Add(VNP(TWO));
}

int main()
{
    MacroName mn;
    init(mn);
    for (int i = 0; i < MAX + 1; i++) {
        std::cout<<i<<": "<<mn.GetName(i)<<std::endl;

    }

    return 0;
}

运行结果和之前一样,符合预期。

尽管如此,对于mn.Add(VNP(ONE));的调用似乎有待优化,因为这里多了一个VNP。——显然是受之前代码的影响而设计出来的。

下面是一种优化:

头文件:

#ifndef __MACRO_NAME_H
#define __MACRO_NAME_H

#include <map>

#define AddMacro(obj, macro) obj.Add(macro, #macro)

extern const char* UNKNOWN;
class MacroName {
public:
    void Add(int v, const char* name);
    const char* GetName(int v) const;

private:
    typedef std::map<int, const char*> VNP_Type;
    VNP_Type m_values;
};

#endif

即增加了一句://删除了不再使用的VNP宏

#define AddMacro(obj, macro) obj.Add(macro, #macro)

调用示例:

// see: http://gcc.gnu.org/onlinedocs/cpp/Stringification.html

#include <iostream>
#include "MacroName.h"

#define ZERO 0
#define ONE 1
#define TWO 2
#define MAX 3

void init(MacroName &mn) 
{
    AddMacro(mn, ZERO);
    AddMacro(mn, ONE);
    AddMacro(mn, TWO);
}

int main()
{
    MacroName mn;
    init(mn);
    for (int i = 0; i < MAX + 1; i++) {
        std::cout<<i<<": "<<mn.GetName(i)<<std::endl;

    }

    return 0;
}

在init中,把面向对象的调用方式,改成了普通的函数调用方式AddMacro。——这可能也是不太习惯的地方。但因为#获取宏名称字符串只能发生在预编译期,所以我们总不能在MacroName的成员函数中做这个事情,因为即便传入宏到成员函数中,其传入行为在(编译&链接之后)执行期,所以已经把宏转换成整数值了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值