o pré-processador C é justificadamente temido e evitado pela comunidade C ++. Funções alinhadas, consts e modelos são geralmente uma alternativa mais segura e superior a a #define
.
A seguinte macro:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
não é de forma alguma superior ao tipo seguro:
inline bool succeeded(int hr) { return hr >= 0; }
Mas as macros têm seu lugar, liste os usos que você encontra para as macros que você não pode fazer sem o pré-processador.
Coloque cada caso de uso em uma resposta separada para que possa ser votado e, se você souber como obter uma das respostas sem o pré-professor, aponte como nos comentários dessa resposta.
c++
c-preprocessor
Motti
fonte
fonte
Respostas:
Como wrappers para as funções de depuração, para passar automaticamente coisas como
__FILE__
,__LINE__
, etc:fonte
__FILE__
e__LINE__
também exigem o pré-processador. Usá-los em seu código é como um vetor de infecção para o pré-processador.Os métodos sempre devem ser códigos completos e compiláveis; macros podem ser fragmentos de código. Assim, você pode definir uma macro foreach:
E use-o da seguinte maneira:
Desde o C ++ 11, isso é substituído pelo loop for baseado em intervalo .
fonte
for_each
era algo desagradável, porque o código em que cada elemento era executado não era local para o ponto de chamada.foreach
, (e eu recomendo, emBOOST_FOREACH
vez de uma solução manual), manter o código próximo ao site de iteração, tornando-o mais legível. Dito isto, uma vez lançada a lambda,for_each
pode ser o caminho a seguir.Os protetores de arquivo de cabeçalho precisam de macros.
Existem outras áreas que precisam de macros? Não são muitos (se houver).
Existem outras situações que se beneficiam de macros? SIM!!!
Um lugar que eu uso macros é com código muito repetitivo. Por exemplo, ao agrupar o código C ++ para ser usado com outras interfaces (.NET, COM, Python, etc ...), preciso capturar diferentes tipos de exceções. Aqui está como eu faço isso:
Eu tenho que colocar essas capturas em todas as funções do invólucro. Em vez de digitar todos os blocos de captura completos, basta digitar:
Isso também facilita a manutenção. Se eu tiver que adicionar um novo tipo de exceção, há apenas um lugar em que preciso adicioná-lo.
Também existem outros exemplos úteis: muitos deles incluem as macros
__FILE__
e__LINE__
pré - processador.De qualquer forma, as macros são muito úteis quando usadas corretamente. Macros não são más - seu mau uso é mau.
fonte
#pragma once
estes dias, por isso duvido guardas são realmente necessárias#pragma once
quebra em muitos sistemas comuns de compilação.void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }
. E no lado da função:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
Na maioria das vezes:
__LINE__
e__FILE__
)fonte
Compilação condicional interna, para superar problemas de diferenças entre compiladores:
fonte
close
funções ou métodos. Então, quando você incluir o cabeçalho desta biblioteca e o cabeçalho nessa macro, você terá um grande problema, não poderá usar a API da biblioteca.#ifdef WE_ARE_ON_WIN32
plz :)Quando você deseja transformar uma string em uma expressão, o melhor exemplo disso é
assert
(#x
transforma o valor dex
em uma string).fonte
Às vezes, as constantes de string são melhor definidas como macros, pois você pode fazer mais com literais de string do que com a
const char *
.por exemplo, literais de string podem ser facilmente concatenados .
Se a
const char *
fosse usado, algum tipo de classe de cadeia teria que ser usada para executar a concatenação no tempo de execução:fonte
Quando você deseja alterar o código de fluxo do programa (
return
,break
econtinue
) em uma função se comporta de maneira diferente do código realmente embutido na função.fonte
-1
ouNULL
. Portanto, uma macro pode reduzir bastante o código padrão aqui.O óbvio inclui guardas
fonte
Você não pode executar um curto-circuito nos argumentos de chamada de função usando uma chamada de função regular. Por exemplo:
fonte
Digamos que ignoraremos coisas óbvias, como guardas de cabeçalho.
Às vezes, você deseja gerar código que precisa ser copiado / colado pelo pré-compilador:
que permite codificar isso:
E pode gerar mensagens como:
Observe que a mistura de modelos com macros pode levar a resultados ainda melhores (ou seja, gerar automaticamente os valores lado a lado com seus nomes de variáveis)
Outras vezes, você precisa do __FILE__ e / ou __LINE__ de algum código, para gerar informações de depuração, por exemplo. A seguir, é um clássico do Visual C ++:
Como no código a seguir:
gera mensagens como:
Outras vezes, você precisa gerar código usando os operadores de concatenação # e ##, como gerar getters e setters para uma propriedade (isso é para casos bastante limitados).
Outras vezes, você irá gerar código que não será compilado se usado por meio de uma função, como:
Qual pode ser usado como
(ainda assim, eu só vi esse tipo de código usado corretamente uma vez )
Por último, mas não menos importante, o famoso
boost::foreach
!!!(Nota: cópia de código / colada na página inicial do impulso)
Qual é (IMHO) muito melhor do que
std::for_each
.Portanto, as macros são sempre úteis porque estão fora das regras normais do compilador. Mas acho que na maioria das vezes vejo um, eles são efetivamente restos do código C nunca convertidos em C ++ apropriado.
fonte
#include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }
Dessa forma, a macro é muito menor.As estruturas de teste de unidade para C ++, como o UnitTest ++, giram em torno das macros do pré-processador. Algumas linhas de código de teste de unidade se expandem para uma hierarquia de classes que não seria divertido digitar manualmente. Sem algo como o UnitTest ++ e a mágica do pré-processador, não sei como você escreveria testes de unidade para C ++ com eficiência.
fonte
Temer o pré-processador C é como temer as lâmpadas incandescentes apenas porque temos lâmpadas fluorescentes. Sim, o primeiro pode ser {eletricidade | tempo do programador} ineficiente. Sim, você pode se queimar (literalmente) por eles. Mas eles podem fazer o trabalho se você lidar com isso adequadamente.
Quando você programa sistemas incorporados, C costuma ser a única opção além do montador de formulários. Depois de programar na área de trabalho com C ++ e depois mudar para destinos menores e incorporados, você aprende a parar de se preocupar com “deselegâncias” de tantos recursos simples de C (incluindo macros) e apenas tentando descobrir o melhor e seguro uso que você pode obter daqueles recursos.
Alexander Stepanov diz :
fonte
Usamos as macros
__FILE__
e__LINE__
para fins de diagnóstico em lançamentos, capturas e registros de exceções ricas em informações, juntamente com scanners de arquivos de log automatizados em nossa infraestrutura de controle de qualidade.Por exemplo, uma macro de lançamento
OUR_OWN_THROW
pode ser usada com parâmetros de tipo e construtor de exceção para essa exceção, incluindo uma descrição textual. Como isso:Obviamente, essa macro lançará a
InvalidOperationException
exceção com a descrição como parâmetro construtor, mas também gravará uma mensagem em um arquivo de log que consiste no nome do arquivo e no número da linha em que a ocorrência ocorreu e sua descrição textual. A exceção lançada receberá um ID, que também será registrado. Se a exceção for capturada em algum outro lugar do código, ela será marcada como tal e o arquivo de log indicará que essa exceção específica foi tratada e, portanto, não é provável que seja a causa de qualquer falha que possa ser registrada posteriormente. Exceções não tratadas podem ser facilmente detectadas por nossa infraestrutura automatizada de controle de qualidade.fonte
Repetição de código.
Dê uma olhada para aumentar a biblioteca de pré-processadores , é um tipo de meta-meta-programação. No tópico motivação, você pode encontrar um bom exemplo.
fonte
Alguns itens muito avançados e úteis ainda podem ser criados usando pré-processador (macros), o que você nunca seria capaz de fazer usando as "construções de linguagem" do c ++, incluindo modelos.
Exemplos:
Fazendo algo tanto um identificador C quanto uma string
Maneira fácil de usar variáveis de tipos de enumeração como string em C
Aumentar a metaprogramação do pré-processador
fonte
stdio.h
esal.h
arquivevc12
para entender melhor.Ocasionalmente, uso macros para definir informações em um único local, mas de diferentes maneiras em diferentes partes do código. É apenas um pouco mal :)
Por exemplo, em "field_list.h":
Em seguida, para uma enumeração pública, pode ser definido apenas para usar o nome:
E em uma função init privada, todos os campos podem ser usados para preencher uma tabela com os dados:
fonte
Um uso comum é para detectar o ambiente de compilação; para o desenvolvimento de plataforma cruzada, você pode escrever um conjunto de códigos para o Linux, por exemplo, e outro para o Windows, quando ainda não existe uma biblioteca de plataforma cruzada para seus propósitos.
Portanto, em um exemplo aproximado, um mutex de plataforma cruzada pode ter
Para funções, elas são úteis quando você deseja ignorar explicitamente a segurança de tipo. Como os muitos exemplos acima e abaixo para fazer ASSERT. Obviamente, como muitos recursos do C / C ++, você pode dar um tiro no pé, mas a linguagem fornece as ferramentas e permite que você decida o que fazer.
fonte
Algo como
Para que você possa, por exemplo, ter
e obtenha o nome do arquivo de origem e o número da linha do problema impressos em seu log, se n for falso.
Se você usar uma chamada de função normal, como
em vez da macro, tudo o que você pode obter é o número da linha da sua função assert impressa no log, o que seria menos útil.
fonte
<cassert>
daassert()
macro, que despeja o arquivo / linha / info função? (em todas as implementações que já vi)Diferente da solução de modelo 'preferencial' discutida em um encadeamento atual, você pode usá-lo como uma expressão constante:
fonte
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Você pode usar #defines para ajudar nos cenários de depuração e teste de unidade. Por exemplo, crie variantes de log especiais das funções de memória e crie um memlog_preinclude.h especial:
Compile seu código usando:
Um link no seu memlog.o para a imagem final. Agora você controla malloc, etc., talvez para fins de registro ou para simular falhas de alocação para testes de unidade.
fonte
Quando você está tomando uma decisão em tempo de compilação sobre o comportamento específico do Compiler / OS / Hardware.
Ele permite que você faça sua interface com os recursos específicos do Comppiler / OS / Hardware.
fonte
Eu uso macros para definir facilmente exceções:
onde DEF_EXCEPTION é
fonte
Os compiladores podem recusar sua solicitação para incorporar.
As macros sempre terão seu lugar.
Algo que acho útil é #define DEBUG para rastreamento de depuração - você pode deixá-lo 1 durante a depuração de um problema (ou até deixá-lo ligado durante todo o ciclo de desenvolvimento) e depois desativá-lo na hora de enviar.
fonte
No meu último trabalho, eu estava trabalhando em um antivírus. Para facilitar a depuração, eu tinha muitos logs presos em todo o lugar, mas em um aplicativo de alta demanda como esse, a despesa de uma chamada de função é muito cara. Então, eu criei essa pequena macro, que ainda me permitia ativar o log de depuração em uma versão de lançamento no site de clientes, sem o custo de uma chamada de função verificaria o sinalizador de depuração e retornaria sem registrar nada, ou se ativado , faria o log ... A macro foi definida da seguinte maneira:
Devido ao VA_ARGS nas funções de log, esse era um bom caso para uma macro como essa.
Antes disso, eu usava uma macro em um aplicativo de alta segurança que precisava dizer ao usuário que eles não tinham o acesso correto e que tipo de sinalizador eles precisavam.
As macro (s) definidas como:
Em seguida, poderíamos simplesmente espalhar as verificações por toda a interface do usuário, e ele informaria quais funções tinham permissão para executar a ação que você tentou executar, se você ainda não tivesse essa função. A razão para dois deles foi retornar um valor em alguns lugares e retornar de uma função nula em outros ...
De qualquer forma, foi assim que os usei e não tenho certeza de como isso poderia ter sido ajudado com modelos ... Fora isso, tento evitá-los, a menos que seja realmente necessário.
fonte
Mais uma macros foreach. T: tipo, c: contêiner, i: iterador
Uso (exibição de conceito, não real):
Melhores implementações disponíveis: Google "BOOST_FOREACH"
Bons artigos disponíveis: Amor condicional: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html
fonte
Talvez o ótimo uso das macros esteja no desenvolvimento independente da plataforma. Pense em casos de inconsistência de tipo - com macros, você pode simplesmente usar diferentes arquivos de cabeçalho - como: --WIN_TYPES.H
--POSIX_TYPES.h
--program.h
Muito legível do que implementá-lo de outras maneiras, na minha opinião.
fonte
Parece que VA_ARGS só foi mencionado indiretamente até agora:
Ao escrever código C ++ 03 genérico e você precisa de um número variável de parâmetros (genéricos), pode usar uma macro em vez de um modelo.
Nota: Em geral, o nome check / throw também pode ser incorporado na hipótese
get_op_from_name
função . Este é apenas um exemplo. Pode haver outro código genérico ao redor da chamada VA_ARGS.Depois de obtermos modelos variados com o C ++ 11, podemos resolver isso "corretamente" com um modelo.
fonte
Eu acho que esse truque é um uso inteligente do pré-processador que não pode ser emulado com uma função:
Então você pode usá-lo assim:
Você também pode definir uma macro RELEASE_ONLY.
fonte
Você pode
#define
constantes na linha de comando do compilador usando a opção-D
ou/D
Isso geralmente é útil na compilação cruzada do mesmo software para várias plataformas, porque você pode fazer com que seus makefiles controlem quais constantes são definidas para cada plataforma.fonte