当前位置:  开发笔记 > 编程语言 > 正文

我可以使用宏的一些技巧?

如何解决《我可以使用宏的一些技巧?》经验,为你挑选了10个好方法。

在我们的遗留代码中,以及我们的现代代码中,我们使用宏来执行代码生成等的漂亮解决方案.我们同时使用###运算符.

我很好奇其他开发人员如何使用宏来做很酷的事情,如果他们根本使用它们的话.



1> Johannes Sch..:

在C中,通常定义宏来做一些获取逐字参数的东西,同时定义函数以便能够透明地获取它的地址.

// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)

// parentheses avoid substitution by the macro
double (sin)(double arg) {
    return sin(arg); // uses the macro
}

int main() {
    // uses the macro
    printf("%f\n", sin(3.14));

    // uses the function
    double (*x)(double) = &sin;

    // uses the function
    printf("%f\n", (sin)(3.14));
}


jder,实际上C(和C++)标准说只有当有一个(紧跟宏名称.如果有的话)之后才能替换像宏这样的函数,就像我们的情况一样,没有替换完成:) 6.10.3/10在C99 TC2(doc n1124 here).在7.1.4中有一个例子作为脚注.
哇!什么与(罪)?你有这个预防宏扩展的参考吗?我从来没有听说过这个.

2> bayda..:

最酷的宏是:断言,包括守卫,__ FILE __,__LINE__.
避免在代码中使用其他宏.

编辑:
只有在没有合法解决方案的情况下才使用宏.



3> 小智..:

还有X Macro习惯用法,它可用于DRY和简单的代码生成:

一个使用尚未定义的宏在头部gen.xa类中定义:

/** 1st arg is type , 2nd is field name , 3rd is initial value , 4th is help */
GENX( int , "y" , 1 , "number of ..." );
GENX( float , "z" , 6.3 , "this value sets ..." );
GENX( std::string , "name" , "myname" , "name of ..." );

然后他可以在不同的地方使用它,为每个#include定义它,通常有不同的定义:

class X
{
public :

     void setDefaults()
     {
#define GENX( type , member , value , help )\
         member = value ;
#include "gen.x"
#undef GENX
     }

     void help( std::ostream & o )
     {
#define GENX( type , member , value , help )\
          o << #member << " : " << help << '\n' ;
#include "gen.x"
#undef GENX
     }

private :

#define GENX( type , member , value , help )\
     type member ;
#include "gen.x"
#undef GENX
}


为什么不把'#undef GENX`放在**gen.x**里面?

4> sth..:

您可以查看Boost.Preprocessor以查找预处理器的许多有趣用途......



5> Mr.Ree..:

用于调试的SHOW():

#define SHOW(X) cout << # X " = " << (X) << endl

扩展参数技巧的双重评估:(例如使用实际行号而不是"__LINE__".)

    /* Use CONCATENATE_AGAIN to expand the arguments to CONCATENATE */
#define CONCATENATE(      x,y)  CONCATENATE_AGAIN(x,y)
#define CONCATENATE_AGAIN(x,y)  x ## y

静态编译时断言.
例如:

#define CONCATENATE_4(      a,b,c,d)  CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d)  a ## b ## c ## d

    /* Creates a typedef that's legal/illegal depending on EXPRESSION.       *
     * Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*".              *
     * (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT)                     \
  typedef char CONCATENATE_4( static_assert____,      IDENTIFIER_TEXT,  \
                              ____failed_at_line____, __LINE__ )        \
            [ (EXPRESSION) ? 1 : -1 ]

用于:

typedef  int32_t  int4;

STATIC_ASSERT( sizeof(int4) == 4, sizeof_int4_equal_4 );

初始化类CodeLocation的实例:(从调用点存储文件/行/函数 - 这只能*通过宏或通过直接访问源点处的__FILE __/__ LINE __/etc宏来完成.)

        /* Note:  Windows may have __FUNCTION__.  C99 defines __func__. */
#define CURRENT_CODE_LOCATION()  \
           CodeLocation( __PRETTY_FUNCTION__, __FILE__, __LINE__ )

随后由MESSAGE/WARN/FAIL宏用作方便的源位置打印机制.例如:

#define WARN_IF_NAN(X)                                      \
  do                                                        \
  {                                                         \
    if ( isnan(X) != 0 )                                    \
      WARN( # X " is NaN (Floating Point NOT-A-NUMBER)" );  \
    if ( isinf(X) != 0 )                                    \
      WARN( # X " is INF (Floating Point INFINITY)" );      \
  } while ( false )

断言/除非宏.您可以通过宏传递任何令牌,包括'=='等运算符.所以构造如下:

ASSERT( foo, ==, bar )

要么

UNLESS( foo, >=, 0, value=0; return false; );

合法.断言/除非宏可以自动添加各种有用的信息,如CodeLocation,堆栈跟踪,或优雅地抛出异常/ coredumping/exiting.


使errno更简单:

#define ERRNO_FORMAT  "errno= %d (\"%s\")"
#define ERRNO_ARGS    errno, strerror(errno)
#define ERRNO_STREAM  "errno= " << errno << " (\"" << strerror(errno) << "\") "

例如printf("Open failed."ERRNO_FORMAT,ERRNO_ARGS);



6> dmityugov..:

我最喜欢的技巧之一是将可变数量的参数传递给宏,以便稍后用于调用类似printf的函数.为此,我指定宏只有一个参数并在没有()的宏体中使用它,但是将所有参数传递给((和)中的宏),因此列表看起来像一个参数.例如,

#define TRACE( allargs) do { printf allargs; } while ( 0)
...
TRACE(( "%s %s\n", "Help", "me"));


您也可以在c99中使用__VA_ARGS__,它在调用者看起来更好.

7> MSN..:

我相信Sean Barrett这个有趣的一个:

#ifndef blah
    #define blah(x) // something fun
    #include __FILE__
    #undef blah
#endif

#ifndef blah
    #define blah(x) // something else that is also fun
    #include __FILE__
    #undef blah
#endif

#ifdef blah
    blah(foo)
    blah(bar)
#endif

一种hacky方法,让预处理器根据您可以通过宏表达的更高级别的结构为您生成代码.


+1:这真的很整齐;就像这里其他答案中描述的“ X宏惯用语”一样,只是您不需要单独的文件。

8> sth..:

记录是经常使用宏的地方之一:

#define LOG(log) \
  if (!log.enabled()) {} \
  else log.getStream() << __FILE__ << "@" << __LINE__ << ": "


log_t errorlog;
...

LOG(errorlog) << "This doesn't look good:" << somedata;



9> kebs..:

对于嵌入式代码,从一个好的技巧embeddedgurus.com 可以处理的二进制值:

B8(01010101) // 85
B16(10101010,01010101) // 43,605
B32(10000000,11111111,10101010,01010101) // 2,164,238,93

这实现了类似于@Ferruccio之前关于BOOST_BINARY的响应的类似目标,尽管有点扩展.

这是代码(copy'n粘贴,未经测试,请参阅链接了解更多详情)

// Internal Macros
#define HEX__(n) 0x##n##LU
#define B8__(x) ((x&0x0000000FLU)?1:0) \
  +((x&0x000000F0LU)?2:0) \
  +((x&0x00000F00LU)?4:0) \
  +((x&0x0000F000LU)?8:0) \
  +((x&0x000F0000LU)?16:0) \
  +((x&0x00F00000LU)?32:0) \
  +((x&0x0F000000LU)?64:0) \
  +((x&0xF0000000LU)?128:0)

// User-visible Macros
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) \
  (((unsigned long)B8(dmsb)<<24) \
  + ((unsigned long)B8(db2)<<16) \
  + ((unsigned long)B8(db3)<<8) \
  + B8(dlsb))

我喜欢宏.调试时非常有趣!



10> 小智..:

我使用宏的主要地方是我自己的测试框架.例如,当我想断言某些代码必须抛出时,我使用这个宏:

#define MUST_THROW( expr )                       
  try {                                
    (expr);                              
    (myth_suite_).Fail( #expr +                    
            std::string( " should throw but didn't" ) );  
  }                                  
  catch( ... ) {                            
  }                                  

并像这样使用它:

MUST_THROW( some_bogus_stuff() );
MUST_THROW( more_bogus_stuff() );

我使用它们的唯一其他地方是在类声明中.我有一个宏:

#define CANNOT_COPY( cls )              \
  private:                              \
    cls( const cls & );                 \
    void operator=( const cls & )       \

我用它来指定一个类不能被复制(或分配):

class BankAccount {

    CANNOT_COPY( BankAccount );
    ....
};

这没有做任何特别的事情,但引起了人们的注意,可以很容易地进行搜索.


这当然使继承方案更加复杂,当你需要一个"真正的"基类时,它会不必要地增加某些编译器上类指针的大小.
推荐阅读
我我檬檬我我186
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有