Pergunta

Eu estou querendo saber sobre o uso prático de #undef em C. Eu estou trabalhando com K & R, e estou até o pré-processador. A maior parte deste foi que o material (mais ou menos) compreendido, mas algo na página 90 (segunda edição) estendeu para mim:

Os nomes podem ser indefinido com #undef, normalmente para garantir que a rotina é realmente uma função, não um macro:

#undef getchar

int getchar(void) { ... }

Esta é uma prática comum para se defender contra alguém #define-ing uma macro com o mesmo nome que a função? Ou isso é realmente mais de uma amostra que não ocorreria na realidade? (EG, ninguém no seu direito, errado nem insana mente deve ser reescrita getchar(), por isso não deve vir para cima.) Com seus próprios nomes de função, você sente a necessidade de fazer isso? Será que a mudança se você está desenvolvendo uma biblioteca para os outros de usar?

Foi útil?

Solução

O que ele faz

Se você ler The Standard C Library do Plauger (1992), você vai ver que o cabeçalho <stdio.h> está autorizada a prestar getchar() e getc() como função semelhante macros (com permissão especial para getc() para avaliar seu argumento ponteiro de arquivo mais de uma vez!). No entanto, mesmo se ele fornece macros, a implementação é também obrigado a provid funções reais que fazem o mesmo trabalho, principalmente para que você pode acessar um ponteiro função chamada getchar() ou getc() e passar isso para outras funções.

Isto é, fazendo:

#include <stdio.h>
#undef getchar

extern int some_function(int (*)(void));

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}

Como escrito, o core_function() é bastante insignificante, mas ilustra o ponto. Você pode fazer a mesma coisa com as macros isxxxx() em <ctype.h> também, por exemplo.

Normalmente, você não quer fazer isso - você normalmente não deseja remover a definição de macro. Mas, quando você precisar a real função, você pode se apossar dela. Pessoas que fornecem as bibliotecas podem emular a funcionalidade da biblioteca padrão C para um bom efeito.

Raramente necessário

Observe também que uma das razões que você raramente precisará usar o #undef explícita é porque você pode chamar a função em vez da macro, escrevendo:

int c = (getchar)();

Como o token após getchar não é uma (, não é uma invocação da função semelhante macro, por isso deve ser uma referência para a função. Da mesma forma, o primeiro exemplo acima, seria compilar e executar corretamente, mesmo sem a #undef.

Se você implementar sua própria função com uma substituição de macro, você pode usar este para um bom efeito, embora possa ser um pouco confuso a menos que explicou.

/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
/* function.c */

…

/* Provide function despite macro override */
int (function)(int c)
{
    return function(c, stdout);
}

A linha de definição da função não invoca a macro porque o token após function não é (. A linha de return faz invocar a macro.

Outras dicas

Macros são muitas vezes utilizados para gerar maior parte do código. É muitas vezes um uso muito localizada e que é seguro #undef quaisquer macros auxiliares no final do cabeçalho especial, a fim de conflitos de nome Evitar tão somente o código gerado real é importado em outro lugar e as macros utilizadas para gerar o código não.

/ Edit: Como um exemplo, eu usei isso para gerar estruturas para mim. O seguinte é um trecho de um projeto real:

#define MYLIB_MAKE_PC_PROVIDER(name) \
    struct PcApi##name { \
        many members …
    };

MYLIB_MAKE_PC_PROVIDER(SA)
MYLIB_MAKE_PC_PROVIDER(SSA)
MYLIB_MAKE_PC_PROVIDER(AF)

#undef MYLIB_MAKE_PC_PROVIDER

Porque #defines pré-processador estão todos em um namespace global, é fácil para os conflitos de namespace para resultado, especialmente quando se utiliza bibliotecas de terceiros. Por exemplo, se você quisesse criar uma função chamada OpenFile, pode não compilar corretamente, porque o <windows.h> arquivo de cabeçalho define o OpenFile token para mapear a qualquer OpenFileA ou OpenFileW (dependendo se UNICODE é definida ou não). A solução correta é #undef OpenFile antes de definir a sua função.

Eu só usá-lo quando uma macro em um arquivo #included está a interferir com uma das minhas funções (por exemplo, ele tem o mesmo nome). Então eu #undef a macro para que eu possa usar a minha própria função.

Embora eu acho que Jonathan Leffler deu-lhe a resposta certa. Aqui está um caso muito raro, onde eu uso um #undef. Normalmente uma macro deve ser reutilizável dentro de muitas funções; é por isso que você define-lo no topo de um arquivo ou em um arquivo de cabeçalho. Mas às vezes você tem algum código repetitivo dentro de uma função que pode ser encurtado com uma macro.


int foo(int x, int y)
{
#define OUT_OF_RANGE(v, vlower, vupper) \
    if (v < vlower) {v = vlower; goto EXIT;} \
    else if (v > vupper) {v = vupper; goto EXIT;}

    /* do some calcs */
    x += (x + y)/2;
    OUT_OF_RANGE(x, 0, 100);
    y += (x - y)/2;
    OUT_OF_RANGE(y, -10, 50);

    /* do some more calcs and range checks*/
    ...

EXIT:
    /* undefine OUT_OF_RANGE, because we don't need it anymore */
#undef OUT_OF_RANGE
    ...
    return x;
}

Para mostrar ao leitor que esta macro é apenas no interior útil da função, ele é indefinido no final. Eu não quero encorajar ninguém a usar essas macros hackish. Mas se você tem que, #undef-los no final.

Esta é uma prática comum para se defender contra alguém # define-ing uma macro com o mesmo nome que a função? Ou isso é realmente mais de uma amostra que não ocorreria na realidade? (EG, ninguém no seu direito, errado, nem mente insana deve ser reescrita getchar (), por isso não deve vir para cima.)

Um pouco de ambos. Boa código não vai exigir o uso de #undef, mas há muita código ruim lá fora, você tem que trabalhar com. #undef pode provar inestimável quando alguém puxa um truque como #define bool int.

Além de corrigir problemas com macros que poluem o namespace global, um outro uso da #undef é a situação em que uma macro pode ser obrigado a ter um comportamento diferente em lugares diferentes. Este não é um cenário realmente comum, mas um casal que vêm à mente são:

  • a macro assert pode tê-lo de definição alterados no meio de uma unidade de compilação para o caso em que você pode querer executar a depuração em alguma parte de seu código, mas não outros. Além assert-se a necessidade de ser #undef'ed para fazer isso, os NDEBUG macro precisa ser redefinido para reconfigurar o comportamento desejado do assert

  • Eu vi uma técnica usada para garantir que globals são definidos exatamente uma vez usando uma macro para declarar as variáveis ??como extern, mas a macro será redefinido para nada para o único caso em que o cabeçalho / declarações são usado para definir as variáveis.

Algo como (não estou dizendo que isso é necessariamente uma técnica boa, só que eu já vi em estado selvagem):

/* globals.h */
/* ------------------------------------------------------ */
#undef GLOBAL
#ifdef DEFINE_GLOBALS
#define GLOBAL
#else
#define GLOBAL extern
#endif

GLOBAL int g_x;
GLOBAL char* g_name;
/* ------------------------------------------------------ */



/* globals.c */
/* ------------------------------------------------------ */
#include "some_master_header_that_happens_to_include_globals.h"

/* define the globals here (and only here) using globals.h */
#define DEFINE_GLOBALS
#include "globals.h"

/* ------------------------------------------------------ */

Se uma macro pode ser def'ed, deve haver um mecanismo para undef.

a utilização do rastreador de memória I define suas próprias macros novos / Eliminar para rastrear informações de arquivo / linha. esta macro divide o SC ++ L.

#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )

Quanto à sua pergunta mais específica:. Namespaces são muitas vezes emul; ated em C prefixando funções de biblioteca com um identificador

macros cegamente undefing vai adicionar confusão, reduzir a manutenção, e pode quebrar as coisas que dependem do comportamento original. Se você foi forçado, pelo menos uso push / pop para preservar o comportamento original em qualquer outro lugar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top