【问题标题】:Pre-preprocessor预处理器
【发布时间】:2009-09-15 11:06:08
【问题描述】:

我想要一个 C 预处理器,它可以从源代码中过滤一些 #define 语句而不更改任何其他内容。

为什么?如果源被分发给另一个客户端,这应该用于从源中删除一些客户端特定的代码。

有人知道现有的解决方案吗?

谢谢! 西蒙

【问题讨论】:

  • 我会将客户端特定的代码移到它自己的标题中。您可以只运行预处理器,但它会扩展所有内容。
  • 这不是一个选项。因为有很多这样的定义,如果它们被移到标题中,它会破坏函数的逻辑。不过谢谢你的建议!
  • 我不明白标准 C 预处理器有什么问题。你能澄清一下吗?

标签: c preprocessor filtering


【解决方案1】:

您可以使用类似 awk 的东西来代替 CPP 吗?在代码中围绕要删除的代码添加一些标志。例如:

(...)
//BEGIN_REMOVE_THIS_CODE

printf("secret code");

//END_REMOVE_THIS_CODE
(...)

然后编写一个 awk 脚本来删除此代码,类似于...

BEGIN { write=1;}
/^\/\/BEGIN_REMOVE_THIS_CODE/ { write=0; next;}
/^\/\/END_REMOVE_THIS_CODE/ { write=1; next;}
    {
    if(write==1) print $0;
    }

【讨论】:

  • 谢谢大家,我想我会试试这个。
  • 正如建议的那样,这涉及修改源代码,以便可以识别特殊模式。如果代码还不存在(所以你可以从这个方案开始),那么它很好。如果您的代码已经存在并使用#ifdef,那么我建议(强烈)使用处理“XXX 始终定义”和“YYY 始终未定义”的众多工具之一。
【解决方案2】:

我建议使用额外的宏语言层进行代码过滤,例如filepp。您可以使用 C 预处理器友好的语法来表达哪些部分属于哪些客户端。

//%ifdef CLIENT_A
  code for client A
//%endif

//%ifdef CLIENT_B
  code for client B
//%endif

//%if "CLIENT_A" || "CLIENT_B"
  code for client A and B
//%endif

'//%' 前缀使您能够编译未修改的代码。在将代码分发给客户之前,您可以运行filepp。

【讨论】:

    【解决方案3】:

    这听起来像我在Is there a C pre-processor which eliminates ifdef blocks based on values defined 中询问的内容。我得到的最佳答案是sunifdef,或“unifdef 之子”,它在一些过度扭曲的条件代码上可靠地为我工作(在各种平台上进行了 20 多年的开发积累的杂物,但没有充分的理论如何进行特定于平台的编译)。

    【讨论】:

      【解决方案4】:

      我认为您不需要为此使用预处理器。如果您的代码中没有嵌套#ifdef,则任何正则表达式引擎都可以删除位于#ifdef CLIENT 和#endif 之间的任何内容(使用非贪婪匹配首先匹配#endif,而不是最后一个)。

      【讨论】:

      • 我不认为正则表达式是适合这项工作的工具。它并不总是您想要匹配的第一个 #endif。如果代码看起来像#if CLIENT... #if FOO... #endif... #endif,你想匹配第二个#endif。如果ifs 是相反的,你想匹配第一个。
      • @Graeme - 这就是我写“如果你没有嵌套的#ifdef's”部分的原因。然而,新的 Perl 正则表达式引擎支持匹配打开/关闭大括号,因此即使在嵌套 #ifdefs 的情况下它也可以完成这项工作
      • @qrdl - 抱歉,我不知何故错过了你的回答。
      【解决方案5】:

      我会将客户端特定的代码放在单独的目录中,或者可能是需要从源代码管理中签出的不同项目的一部分。

      放置一个函数调用,该函数调用将被存根或(我忘记了正确的术语)松散链接,以便可以将另一个函数放置在它的位置。

      【讨论】:

        【解决方案6】:

        如果你使用 gcc,那么你可以使用:

        gcc <insert files here> -E
        

        -E 选项告诉 gcc 只预处理源代码,而不是编译它们。

        或者,您可以使用 grep 过滤掉特定文件,让预处理器只对它们进行处理。

        grep -r '#define CLIENT_CODE' ./*.h 
        

        【讨论】:

        • 注意:强烈建议你不要去做你说你想做的事。如果您的客户端使用不同的系统,则预先处理源可能会破坏兼容性宏。
        • 投反对票的人愿意发表评论,解释这个答案有什么问题吗?我给它+1,因为它对我来说看起来很完美。即使您没有使用 gcc 作为编译器,您仍然可以下载它并将其用作此任务的预处理器。
        • @MarkJ - Aviraldg 的评论解释了为什么这不是一个理想的解决方案。我自己并没有否决它,但是除非应用程序将构建在与完成预处理的系统相同的系统上,否则这可能会导致最好的编译失败,最坏的情况是很难找到错误。
        • @MarkJ:我没有投反对票,但如果是正数,我可能会投反对票。问题是C预处理器对源代码进行了预处理,包括的内容等等。这不太可能是提问者的想法——他们想要除了一些位之外的来源。所以,我支持 Graeme 和 Aviraldg - 这不是正确的答案。
        【解决方案7】:

        您也可以试试unifdef,它比 sunifdef 更简单。

        【讨论】:

          【解决方案8】:

          你为什么不做类似的事情:

          client_a_specific_functions_definition.c

          double discount_for_paying_upfront() { return 0.1; };
          // ...
          

          client_b_specific_functions_definition.c

          double discount_for_paying_upfront() { return 0.05; };
          // ...
          

          当您分发代码时,只需选择具有特定定义的正确文件即可。

          然后您将创建一个头文件以将其包含在您需要访问客户端特定代码的位置,例如:

          client_functions.h

          #pragma once 
          
          double discount_for_paying_upfront();
          
          #define stringify(x) #x
          #define FILE2(a) stringify(client_##a##_specific_functions_definition.c)
          #define FILE(a) FILE2(a)
          
          #include FILE(CLIENT_NAME)
          
          #undef stringify
          #undef FILE2
          #undef FILE
          

          然后在你的main.c 中说你#include "client_functions.h"。你可以编译它:

           gcc -DCLIENT_NAME=a main.c -o a.exe
           gcc -DCLIENT_NAME=b main.c -o b.exe
          

          【讨论】:

            【解决方案9】:

            据我所知...预处理器可以作为单独的步骤运行(使用正确的编译器选项)。这样你就可以对处理后的代码做任何你想做的事情。

            【讨论】:

            • 嗨,这是我首先想到的。但是我还没有找到一个简单的选项来使用预处理器来替换特定的#define 语句。你有这方面的例子吗?
            • 看起来 aviraldg 在编译器选项和所有方面都击败了我。
            • 附带说明...在过去的美好时光中,预处理器是一个单独的步骤和一个单独的可执行文件。很有可能仍然可以找到单独的预处理器。
            • @reiner 在某些环境中仍然是一个单独的步骤。例如,GCC 本身并不进行预处理——它调用 cpp 来完成这项工作。 cpp 可作为单独的二进制文件使用。
            • @reiner:您的回答被否决了,因为尽管您可以单独运行 C 预处理器(通过编译器选项或作为单独的可执行文件),但提问者想要的不是预处理器所做的。例如,如果代码包含“#include ”,则输出仍应包含“#include ”,并且宏(如getchar())应保持不变。另一方面,一些代码位 - 那些受#ifdef CLIENT / remove this / #else / keep this / #endif 保护的代码应该只有输出的“keep this”部分。标准预处理器无法做到这一点。
            猜你喜欢
            • 2015-07-31
            • 2012-03-11
            • 2010-09-07
            • 2015-04-15
            • 2011-07-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多