【问题标题】:GCC Macro with Conditional Expansion?具有条件扩展的 GCC 宏?
【发布时间】:2014-01-10 00:51:51
【问题描述】:

我几乎可以肯定答案是否定的。但我很固执;我真的很想实现这个功能,周期很重要(阅读:嵌入式)。

目标:

对于这个嵌入式应用程序,我有一个需要内联的时钟配置序列。到处都是。我想用宏来做到这一点。

问题

时钟配置的执行顺序随参数“f”(新时钟频率)而变化。

- if (f>10)  do A first
- if (f<=10) do A last
- f is a compile time constant.

示例

#define setup_post10(f) doA(f);  \
                        doB(f);  \
                        doC(f)

#define setup_pre10(f)  doB(f);  \
                        doC(f);  \
                        doA(f)

问题

如何通过简单的设置 (f) 来封装它?例如。我想:

#define set(f)  #if(f>10) \
                    setup_post10(f)  \
                #else                \
                    setup_pre10(f)   \
                #end                 

但是在宏定义中使用预处理器指令(例如#if)是无效的(据我所知)。

有没有办法做到这一点?对于我的应用程序,每个周期(和闪存字节)都是宝贵的;我很固执;我希望在我的代码中具有这种抽象级别。

谢谢!

-贾斯汀

【问题讨论】:

标签: c gcc macros


【解决方案1】:

如果每个周期都很宝贵,那么您应该在汇编中进行编码。 :)

如果您信任您的 C 编译器优化器,那么就这样做

   #define set(f)  if((f)>10) setup_post10(f)  \
            else setup_pre10(f)   

如果f 是一个常量,那么编译器将只选择一个条件分支进行编译,假设您启用了最小优化。例如set(11) 应该直接转换为对setup_post10(11) 的调用

【讨论】:

  • 在任何示例中,请确保在适当的时候用括号保护宏参数。
【解决方案2】:

查看 Boost 预处理器库 (http://www.boost.org/doc/libs/1_55_0/libs/preprocessor/doc/index.html)。它包括一种做这些事情的方法——特别是,它有BOOST_PP_LESS

虽然如果您只针对特定的编译器,使用函数而不是宏可能会更容易,并强制它内联。然后,您可以使用简单的if,并依靠优化器将其优化为常量参数。我认为,较新的 GCC 版本甚至可以断言某些值是编译器时常量,这几乎可以保证 if 永远不会被实际发出。

【讨论】:

  • 要添加到这一点,请确保整个函数定义在编译时对被调用者可用(将其粘贴在标头中),您甚至不需要告诉编译器内联它。只要有意义,它就会这样做。
  • 有时你从一个问题开始的时间过长。让编译器优化 if/else 效果很好。谢谢!!
【解决方案3】:

如果您对 GCC 以外的编译器的可移植性不感兴趣,您可以使用 GCC 的“语句表达式”扩展来很好地处理这个问题:

#define setup_post10(f) ({ doA(f); doB(f); doC(f); 0; })

#define setup_pre10(f)  ({ doB(f); doC(f); doA(f); 0; })

#define set(f) (((f) <= 10) ? setup_pre10(f) : setup_post10(f))

如果在条件运算符的控制表达式中测试的表达式是编译时常量,编译器将只为将要执行的表达式发出代码,而忽略永远不会执行的分支的代码(可能不在-O0 优化级别)。

【讨论】:

  • 您可以通过删除两个设置宏中的大括号并用逗号替换分号来避免 GCC 主义,不是吗?
  • @JonathanLeffler: 可能——我不想考虑如果doX() 中的任何一个是时髦的宏是否会产生影响,所以我采用了我认为简单的方法出来。
  • 我认为#define setup_post10(f) ((doA(f)),(doB(f)),(doC(f)),0) 应该是防弹的。显然,受制于诸如doA之类的宏生成常规代码——扩展中没有不匹配的括号等。但是不管是不是这样,都会有问题。
【解决方案4】:

我还发现在头文件中使用静态内联函数可以获得相同的结果。

示例

static inline set(uint8_t f) {

    if(f > 10) {
        doA();
        doB();
        doC();
    } else {
        doB();
        doC();
        doA();
    {
}

我发现这种方法可以生成更具可读性的代码,所以我现在正在使用它。虽然我必须说它违反了我的一条黄金法则不要将代码引入头文件*

贾斯汀

*关于“头文件中的代码”是否有任何最佳实践?我记得我曾经参与过的一个项目的恐怖故事,其中数百行存在于头文件中,结构和格式都很差。因此,我在这里使用这种静态内联方法很矛盾。想法?

【讨论】:

    猜你喜欢
    • 2011-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多