如何让 C 预处理器在编译期间执行代码?

Posted

技术标签:

【中文标题】如何让 C 预处理器在编译期间执行代码?【英文标题】:How to have the C preprocessor execute code during compilation? 【发布时间】:2011-09-30 02:09:01 【问题描述】:

我目前正在处理一个代码项目,该项目需要我用这些字符串的哈希值替换某些字符串。鉴于这些字符串在运行时不会改变,从效率方面来看,让 c 预处理器对我声明在编译时进行散列的每个字符串运行我的散列函数将是有利的。

有没有办法让 C 预处理器在编译时运行我的哈希函数?

我知道这不像我上面描述的那样工作,但只是为了了解我要去哪里,这里有一些使用宏的伪代码。想象一下,预处理器不是简单地扩展宏,而是运行哈希函数并将其扩展为该哈希函数的返回值:

    #include <iostream>
    #include <string>

    #define U64_HASH(inputString) getU64HashCode(inputString)

    //my hash function
    unsigned long long getU64HashCode (string inputString)
    
        /*code*/
    

    int main()
    
        cout << U64_HASH("thanks for helping me") << endl;
        return 0;
    

同样,理想情况下 cout &lt;&lt; U64_HASH("thanks for helping me") &lt;&lt; endl; 将扩展为 cout &lt;&lt; 12223622566970860302 &lt;&lt; endl;

我写了一个头文件生成器,这对这个项目很有效。

最终解决方案

我决定在这个项目中使用John Purdy's perl script,因为它简直太棒了,并且允许我将我想要的输出直接提供给我的编译器。非常感谢,约翰。

【问题讨论】:

并非没有一些疯狂的魔法。 C++11 具有用户定义的文字和 constexpr。这些可能会有所帮助。 你总是可以#define 这些字符串作为它们的哈希值? AFAIK C 预处理器没有任何运行代码的能力。 内置的 C 预处理器无法做到这一点。 MSN 的答案是您将得到的最接近的答案。 为了将来的参考,我写了a very handy Perl script来完成这类任务。 【参考方案1】:

实现此目的的一种方法是将所有字符串放入头文件中,并为其命名:

// StringHeader.h
#define   helloWorld              "Hello World"
#define   error_invalid_input     "Error: Invalid Input"
#define   this_could_get_tedious  "this could get tedious"

然后你可以使用这些字符串:

#include "StringHeader.h"
std::cout << this_could_get_tedious << std::endl;

然后你可以在你的StringHeader.h 上运行一个程序来散列每个字符串,并生成一个替换头文件:

// Generated StringHeader.h
#define   helloWorld              097148937421
#define   error_invalid_input     014782672317
#define   this_could_get_tedious  894792738384

起初看起来非常手动且乏味,但有办法将其自动化。

例如,您可以编写一些东西来解析您的源代码,寻找“带引号的字符串”。然后它可以命名每个字符串,将其写入单个StringHeader.h,并用新的命名字符串常量替换内联引用的字符串。作为创建文件时的附加步骤,您可以散列每个字符串 - 或者您可以在创建文件后一次性拥有该文件。这可以让您创建文件的散列和非散列版本(创建非散列调试版本和散列发布版本可能很好)。

如果您确实尝试这样做,则查找字符串的初始解析器将不得不处理边缘情况(cmets、#include 行、重复字符串等)。

【讨论】:

【参考方案2】:

如果编译器曾经支持这一点,C++11 有 user defined literals:

constexpr unsigned long long operator "" U64_HASH_(
    const char *literal_string)  ... 

#define U64_HASH(inputString) inputString U64_HASH_

constexpr:

constexpr unsigned long long operator "" U64_HASH(
    const char *literal_string)  ... 

【讨论】:

这是否保证返回字符串文字的哈希值?【参考方案3】:

如果您无法让预处理器为您执行此操作,您可以先编写自己的预处理器来执行此步骤。

【讨论】:

是的,我正在考虑,如有必要,我会这样做,但如果可以的话,我真的很想使用标准预处理器。【参考方案4】:

没有办法强制它,但如果你的编译器足够好,它可以做到。使用它的优化选项并研究调试器中代码的反汇编,看看它们中的任何一个是否能让你实现你想要的。

【讨论】:

你能说出一个编译器来完成你所说的吗? 您所要做的就是在宏本身中写入完整的散列 - 然后编译器应该将其视为常量表达式并替换该值(假设优化设置允许)。简单!*(* 困难) @David:我认为 DMD 编译器可以通过 CTFE 做到这一点 :) 虽然我也很好奇它是否可以在 C++ 中实现。没听说过 @geofftnz:为Easy!* 部分+1。真的很有趣:) @DavidNehme:我没有在很多编译器上尝试过这段代码,但我已经看到 MSVC++ 和 gcc 做了一些非常好的优化,因此我不能说这是不可能的。也许如果字符串作为 const char* 而不是字符串传递,有些人可以弄清楚。

以上是关于如何让 C 预处理器在编译期间执行代码?的主要内容,如果未能解决你的问题,请参考以下文章

C语言编译链接生成可执行文件四大步骤:预处理->编译->汇编->链接

如何让 C 语言中的 ANTLR3.5 生成的解析器在 MVS EBCDIC 环境中工作?

C语言_宏

如何让VS2017忽略特定编译器的关键字?

C语言——编译器如何将C源文件编译成可执行代码?

函数内联inline