11

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

为了避免破坏大量预先存在的单元测试,库必须做的一件事是,对于每种类型,生成该类型的名称以全部大写进行打印。例如,类型“flag”必须打印为“FLAG”。

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

#define flag_ALLCAPSNAME 标志

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

目前,我已经一起破解了这个:

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 循环宏(就像这个),但是我不能改变由#flag 生成的字符串文字的内容,并且在任何情况下,我的循环宏都需要一个字符指针列表来迭代(即它迭代列表,而不是索引等)。

想法?

4

2 回答 2

15

在可移植的 C99 中不可能有一个将常量字符串转换为所有大写字母的宏(特别是因为字母的概念与字符编码有关。UTF8 字母与 ASCII 字母不同)。

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

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

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

  • 使用GCC 扩展可能有

    #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 现在是一个非活动项目)。或者用你自己的GCC 插件在 C++ 中编写代码(注意,它只适用于一个给定的 GCC 版本)。

于 2012-09-17T08:43:51.710 回答
1

完全使用 c 预处理器是不可能做到这一点的。这样做的原因是预处理器将输入作为(原子)读取,并pp-tokens从中组成输出。预处理器没有任何结构可以以pp-token任何方式将 a 分解为单个字符(无论如何,没有人可以帮助您)。

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

在某种意义上,唯一允许您分解 a 的构造pp-token是通过一些表达式。然而,这些表达式只能在算术类型上工作,这就是为什么它们在这里对你没有帮助。

您的方法通过使用 C 语言结构来规避这个问题,即您在运行时进行转换。预处理器唯一要做的就是插入 C 代码来转换字符串。

于 2016-04-05T11:29:21.300 回答