是否可以使用预处理器(宏)将C字符串文字转换为大写?

时间:2012-09-17 08:28:28

标签: c macros c-preprocessor

忽略有时候有更好的非宏方式(我有充分的理由,遗憾的是),我需要使用宏编写一大堆通用代码。本质上是一个宏库,它将为某些预先指定的类型生成大量函数。

为了避免破坏大量预先存在的单元测试,库必须做的一件事是,对于每种类型,都要在所有大写字母中生成该类型的名称以进行打印。例如。类型“标志”必须打印为“标记”。

我可以手动写出每种类型的常量,例如

#define flag_ALLCAPSNAME FLAG

但这并不理想。我希望能够以编程方式执行此操作。

目前,我一直在攻击这个:

char capname_buf[BUFSIZ];
#define __MACRO_TO_UPPERCASE(arg) strcpy(capname_buf, arg); \
 for(char *c=capname_buf;*c;c++)*c = (*c >= 'a' && *c <= 'z')? *c - 'a' + 'A': *c;
__MACRO_TO_UPPERCASE(#flag)

我在某种程度上做了我想做的事情(即在这段代码之后,capname_buf将“FLAG”作为其内容),但我更喜欢一种允许我使用宏来定义字符串文字的解决方案,避免使用需要这个愚蠢的缓冲区。

我看不出怎么做,但也许我错过了一些明显的东西?

我有一个可变参数的foreach循环宏(如this one),但我不能改变#flag生成的字符串文字的内容,无论如何,我的循环宏需要一个列表字符指针迭代(即它遍历列表,而不是索引等)。

思想?

2 个答案:

答案 0 :(得分:15)

便携式C99中不可能有一个宏将常量字符串转换为全部大写字母(特别是因为字母的概念与字符编码有关.UTF8字母与ASCII字母不同)。

但是,您可以考虑其他一些解决方案。

  • 自定义您的编辑器。例如,您可以编写一些emacs代码,根据需要更新每个C源文件。

  • 在C源代码上使用一些预处理器(可能是一个简单的C代码生成器脚本,它会在某些#define - d文件中发出一堆#include

    < / LI>
  • 使用GCC extensions或许

    #define TO_UPPERCASE_COUNTED(Str,Cnt)
    #define TO_UPPERCASE(Str) TO_UPPERCASE_COUNTED(Str,__COUNT__) {( \
       static char buf_##Cnt[sizeof(Str)+4]; \
       char *str_##Cnt = Str; \
       int ix_##Cnt = 0; \
       for (; *str_##Cnt; str_##Cnt++, ix_##Cnt++) \
         if (ix_##Cnt < sizeof(buf_##Cnt)-1) \
             buf_##Cnt[ix_##Cnt] = toupper(*str_##Cnt); \
       buf_##Cnt; )}
    
  • 自定义GCC,可能使用MELT(一种特定于域的语言来扩展GCC),为您的__builtin_capitalize_constant提供工作(编辑:MELT是现在是不活跃的项目)。或者使用C ++编写自己的GCC plugin代码(警告,它只适用于一个给定的GCC版本)。

答案 1 :(得分:1)

使用c预处理器完全无法完成此操作。原因是预处理器将输入读取为(原子)pp-tokens,从中组成输出。预处理器没有任何构造可以以任何方式将pp-token分解为单个字符(无论如何都没有人可以帮助你)。

在您的示例中,当预处理器读取字符串文字"flag"时,预处理器基本上是一个原子文本块。它有结构有条件地去除这些块或将它们粘合在一起形成更大的块。

允许您在某种意义上分解pp-token的唯一构造是通过一些表达式。但是这些表达式只适用于算术类型,这就是为什么它们在这里无法帮助你。

您的方法通过使用C语言构造来解决此问题,即您在运行时进行转换。然后预处理器唯一要做的就是插入C代码来转换字符串。