Pergunta

No nosso código legado, bem como o nosso código moderno, usamos macros para executar soluções astuciosas como gerações de código, etc. E nós fazer uso de ambos os # e operadores ##.

Estou curioso como outros desenvolvedores usam macros para fazer coisas legais, se eles usá-los em tudo.

Foi útil?

Solução

Em C, é comum para definir macros que fazer algumas coisas recebendo o argumento literal, e ao mesmo tempo definir funções para ser capaz de obter o endereço dele de forma transparente.

// 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));
}

Outras dicas

Coolest macro é: assert, incluem guardas, __FILE__, __LINE__
. Evite usar outra macro em seu código.

EDIT:
Use macros somente quando você não tem solução jurídica w / o-los.

Há também a expressão X Macro que pode ser útil para DRY e geração de código simples:

se define em um cabeçalho gen.x uma espécie de tabela usando um ainda não definido macro:

/** 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 ..." );

Então, ele pode usá-lo em lugares diferentes defini-la para cada # include com uma definição geralmente diferente:

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
}

Você pode ter um olhar para Boost.Preprocessor para encontrar o lote de usos interessantes do pré-processador ...

SHOW () para depurar:

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

O duplo-avaliação para expandir a argumentos truque: (por exemplo Use o número da linha real e não "__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

estático em tempo de compilação afirmações.
por exemplo:.

#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 ]

Usado via:

typedef  int32_t  int4;

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

A inicializar uma instância da classe CodeLocation: (Armazenamento de Arquivo / Linha / Função do ponto de invocação - isso pode * APENAS * ser feito com uma macro ou acessando diretamente o __FILE __ / __ LINHA __ / etc macros no ponto de origem. )

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

Em seguida usado por mensagem / WARN / macros falhar como um mecanismo de impressão fonte-localização conveniente. Por exemplo:

#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 / A menos que macros. Você pode passar qualquer forma, incluindo operadores como '==', através de uma macro. Então construções como:

ASSERT( foo, ==, bar )

ou

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

são legais. Assert / A menos que as macros podem adicionar automaticamente todos os tipos a informação agradável útil como CodeLocation, rastreamentos de pilha, ou lançar exceções / coredumping / sair graciosamente.


Fazendo errno simplier:

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

por exemplo. printf ( "Open falhou." ERRNO_FORMAT, ERRNO_ARGS);

Um dos meus truques favoritos é uma maneira de passar um número variável de argumentos para macros, para ser usado mais tarde em chamar printf-like funções, por exemplo. Para fazer isso, eu especificar que a macro tem apenas um parâmetro e usá-lo no corpo da macro sem (), mas passar todos os parâmetros para a macro no ((e)), assim que a lista se parece com um único argumento. Por exemplo,

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

Logging é um lugar onde as macros são particularmente frequentemente utilizados:

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


log_t errorlog;
...

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

I crédito Sean Barrett para um presente divertido:

#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

Uma maneira hacky para obter o pré-processador para gerar o código para você com base em uma estrutura de nível superior que você pode expressar através de macros.

A principal lugar que eu utilizar macros está em meu próprio framework de testes. Por exemplo, quando eu quero afirmar que algum código deve jogar, eu uso essa macro:

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

E usá-lo como este:

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

O único outro lugar que eu usá-los é em declarações de classe. Eu tenho uma macro:

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

que eu uso para especificar que uma classe não pode ser copiado (ou atribuído):

class BankAccount {

    CANNOT_COPY( BankAccount );
    ....
};

este não faz nada de especial, mas chama a atenção das pessoas e pode ser facilmente procurado.

Para código incorporado, um bom truque de embeddedgurus.com permite-lhe lidar com valores binários:

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

Esta atingir metas semelhantes, como a resposta anterior da @Ferruccio sobre BOOST_BINARY, embora um pouco expandida.

Aqui está o código (copy'n colado, não testado, consulte o link para mais detalhes)

// 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))

Eu gosto de macros. Muito divertido quando a depuração!

Muitas vezes envolver coisas como sonar de depuração em uma macro simples que permite que ele seja compilado fora de compilações:

#ifdef DEBUG
#define D(s) do { s; } while(0)
#else
#define D(s) do {/**/} while(0)
#endif

Uso mais tarde é tipicamente algo como:

D(printf("level %d, condition %s\n", level, condition));

O idioma do{}while(0) está lá para questões evitar que possam resultar acidentalmente fazendo um uso de D(...) o único conteúdo de uma condicional ou loop. Você não quer um código como este para significar a coisa errada, afinal:

for(i=1;i<10;++i) D(printf("x[%d]=%f\n",i,x[i]));
SomeReallyExpensiveFunction(x);

Se eu pudesse fazer nesse caso lançar um erro, eu o faria, mas o pré-processador teria que ser em si um compilador completo para dizer que a macro D() era o único conteúdo de um corpo do laço.

Eu também sou um grande fã de afirmações de tempo de compilação. Minha formulação é ligeiramente diferente, mas não tem vantagens reais sobre os outros que eu vi. A chave é para formar um typedef um nome único que lança um erro se a condição afirmado é falsa, e não o contrário. Em cassert.h temos:

/*! \brief Compile-time assertion.
 *
 *  Note that the cassert() macro generates no code, and hence need not
 *  be restricted to debug builds.  It does have the side-effect of
 *  declaring a type name with typedef.  For this reason, a unique
 *  number or string of legal identifier characters must be included
 *  with each invocation to avoid the attempt to redeclare a type.
 *
 *  A failed assertion will attempt to define a type that is an array
 *  of -1 integers, which will throw an error in any standards
 *  compliant compiler. The exact error is implementation defined, but
 *  since the defined type name includes the string "ASSERTION" it
 *  should trigger curiosity enough to lead the user to the assertion
 *  itself.
 *
 *  Because a typedef is used, cassert() may be used inside a function,
 *  class or struct definition as well as at file scope.
 */
#define cassert(x,i) typedef int ASSERTION_##i[(x)?1:-1]

E em algum arquivo de origem, em qualquer lugar um typedef seria legal:

#include "cassert.h"
...
cassert(sizeof(struct foo)==14, foo1);
...

A mensagem de erro resultante é muitas vezes obscuro, mas irá conter o fragmento de identificador que permite a linha de ofender a ser descoberto pela força bruta.

Eu tenho sido culpado de usar o pré-processador em lugares onde escrevendo um utilitário de geração de código pode ter sido a resposta preferida, muito parecido com o código em outra resposta que gerou muita caldeira-plate com base nas peças únicas de um membro da enumeração de nome. Isso é especialmente útil quando se escreve um monte de cola mensagem-expedição para ser compilado em C.

literais estrutura com valores padrão (que não são zero), utilizando C99 variádica macros

struct Example {
   int from;
   int to;
   const char *name;
}

#define EXAMPLE(...) ((struct Example){.from=0, .to=INT_MAX, .name="", __VA_ARGS__})

usando EXAMPLE(.name="test") usa os valores padrões, exceto para a substituição explícita de name. Este sombreamento com mais tarde menciona do mesmo membro é bem definido na norma.

Pode-se simplificar as coisas repetitivas para o IE. listas enum

enum {
  kOneEnum,
  kTwoEnum,
  kThreeEnum,
  kFourEnum
};

... e depois fazer um caso chave ao longo de um caminho estruturado

#define TEST( _v ) \
    case k ## _v ## Enum: \
      CallFunction ## _v(); \
      break;

switch (c) {
    TEST( One   );
    TEST( Two   );
    TEST( Three );
    TEST( Four  );
}

Nota:. Claro que isso poderia ser feito com uma matriz ponteiro de função, mas isso abre um pouco mais de flexibilidade para adicionar parâmetros e também usar as expansões de cordas com o single de hash

... ou para teste em cordas para obter o valor enum direito

int value = -1;
char *str = getstr();

#define TEST( _v ) \
    if (!strcmp(# _v, str)) \
        value = k ## _v ## Enum

TEST( One   );
TEST( Two   );
TEST( Three );
TEST( Four  );

Você pode usar macros para definir a mesma funcionalidade com diferentes tipos de dados. Por exemplo:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#define DEFINE_BITS_STR(name, type)               \
char *bits_str_##name(type value)                 \
{                                                 \
    int len = sizeof(type) * CHAR_BIT;            \
    char *result;                                 \
    type n;                                       \
    int i;                                        \
                                                  \
    result = (char *)calloc(len+1, sizeof(type)); \
    if(result == NULL)                            \
        return NULL;                              \
                                                  \
    memset(result, '0', len);                     \
    result[len] = 0x00;                           \
                                                  \
    n = value;                                    \
    i = len;                                      \
    while(n)                                      \
    {                                             \
        if(n & 1)                                 \
            result[i] = '1';                      \
                                                  \
        n >>= 1;                                  \
        --i;                                      \
    }                                             \
                                                  \
    return result;                                \
}

DEFINE_BITS_STR(uchar, unsigned char)
DEFINE_BITS_STR(uint, unsigned int)
DEFINE_BITS_STR(int, unsigned int)

int main()
{
    unsigned char value1 = 134;
    unsigned int value2 = 232899;
    int value3 = 255;
    char *ret;

    ret = bits_str_uchar(value1);
    printf("%d: %s\n", value1, ret);

    ret = bits_str_uint(value2);
    printf("%d: %s\n", value2, ret);

    ret = bits_str_int(value3);
    printf("%d: %s\n", value3, ret);

    return 1;
}

Neste exemplo define três funções (bits_str_uchar(), bits_str_uint(), bits_str_int()) que pega três tipos de dados diferentes (unsigned char, unsigned int, int). No entanto, todos retornam uma string que contém os bits do valor passado.

Quando você implementar um servidor COM, você tem que cuidar de todas as exceções seu código poderia jogar -. Deixando uma exceção através fronteira método COM, muitas vezes, travar o aplicativo chamando

Métodos suportes são úteis para isso. Não há um suporte de abertura, que é uma macro contendo "tentativa" e um suporte de fecho que contém um conjunto de es "de captura", embrulho de excepções em ErrorInfo e HRESULTs produtoras.

A partir do projeto CrashRpt, precisa enganar a ampliar macros e define:

#define WIDEN2(x) L ## x 
#define WIDEN(x) WIDEN2(x)
std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

A maioria (todos?) Os quadros C ++ unidade de teste são construídos sobre macros. Usamos UnitTest ++ . Check it out para ver todos os tipos de macros de fantasia.

O BOOST_BINARY executa macro alguns NívelC pré-processador de fraude para dar C ++ a capacidade de expressar constantes numéricas em binário. É limitado a 0-255 no entanto.

pthreads utilidade macros são IMHO particularily impressionante.

Quando eu trabalhar em enormes c / c ++ estruturas aninhadas como o usado para 3GPP RRC / NBAP / RNSAP, eu sigo esse truque para fazer o olhar código limpo.

struct leve1_1
{
  int data;

  struct level2
  {
    int data;

    struct level3
    {
      int data;
    } level_3_data;

  } level_2_data;

} level_1_data;

level_1_data.data = 100;

#define LEVEL_2 leve1_1_data.level_2_data
LEVEL_2.data = 200;

#define LEVEL_3 LEVEL_2.level_3_data
LEVEL_3.data = 300;

#undef LEVEL_2
#undef LEVEL_3

Isso vai tornar a vida mais fácil durante a manutenção time..also em tempo de design e código será legível.

convertendo-os para uma construção da linguagem para melhorar a segurança de tipo e capacidade de depuração.

void _zero_or_die(int v, const char* filename, int line)
{
    if (v != 0)
    {
       fprintf(stderr, "error %s:%d\n", filename, line);
       exit(1);
    }
}

#define ZERO_OR_DIE_ for (int _i=1; _i == 1; _zero_or_die(_i, __FILE__, __LINE__)) _i=



ZERO_OR_DIE_   pipe(fd);
ZERO_OR_DIE_   close(0);
ZERO_OR_DIE_   sigaction(SIGSEGV, &sigact, NULL);
ZERO_OR_DIE_   pthread_mutex_lock(&mt);
ZERO_OR_DIE_   pthread_create(&pt, NULL, func, NULL);

No micro controladores é comum código de depuração usando UART, como pontos de interrupção de hardware têm muitas desvantagens.

Esta é uma macro simples que provou ser muito útil:

#define DEBUG_OUT(value) sprintf(uartTxBuf, "%s = 0x%04X\n", #value, value);\
                         puts_UART((uint16_t *) uartTxBuf)

Exemplo de uso:

for (i=0; i < 4; i++)
{
    DEBUG_OUT(i);
    DEBUG_OUT(i % 3);
}

fluxo Recieved:

i = 0x0000
i % 3 = 0x0000
i = 0x0001
i % 3 = 0x0001
i = 0x0002
i % 3 = 0x0002
i = 0x0003
i % 3 = 0x0000

Sim, é bruto e inseguro. É aplicada somente até que o bug é isolado, por isso esta macro não faz mal.

Muitas vezes eu uso isso. Eu tenho um debug.h cabeçalho definir como o seguinte:

#ifndef DEBUG_H
#define DEBUG_H
    #ifdef DEBUG
    #define debuf if(1)
    #else
    #define debug if(0)
    #endif
#endif

e, em seguida:

debug {
   printf("message from debug!");
}

Se você quiser obter mensagem "message from debug!", compilação com:

gcc -D DEBUG foo.c

Caso contrário, nada acontece. Gcc é um compilador muito inteligente. Se DEBUG não está definido, o if(0) gerado (código morto) será removido do seu código com algumas otimizações diante.

Você ainda pode fazer mais:

debug 
{
   pritnf("I'm in debug mode!\n");
} 
else 
{
  printf("I'm not in debug mode\n");
}

Alguns dias atrás eu vi o href="http://dlang.org/index.html" linguagem rel="nofollow"> programação D fornecer um recurso muito similar também.

Se você acha que o anterior, sem contexto, pode-se definir pensa como

#define in_debug if(1)
#define not_debug else

E, em seguida,

in_debug {
  printf("I'm in debug mode!");
}
not_debug {
  printf("Not in debug mode!");
}

Em macros, é muito fácil de do fluxo de controle porque é substituição de texto apenas. Aqui está um exemplo com um loop for:

#include <stdio.h>

#define loop(i,x) for(i=0; i<x; i++)

int main(int argc, char *argv[])
{
    int i;
    int x = 5;
    loop(i, x)
    {
        printf("%d", i); // Output: 01234
    } 
    return 0;
} 
scroll top