Le préprocesseur C est à juste titre craint et rejeté par la communauté C ++. Les fonctions intégrées, les consts et les modèles sont généralement une alternative plus sûre et supérieure à un #define
.
La macro suivante:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
n'est en aucun cas supérieur au type safe:
inline bool succeeded(int hr) { return hr >= 0; }
Mais les macros ont leur place, veuillez lister les utilisations que vous trouvez des macros que vous ne pouvez pas faire sans le préprocesseur.
Veuillez mettre chaque cas d'utilisation dans une réponse distincte afin qu'il puisse être voté et si vous savez comment obtenir l'une des réponses sans le préprosesseur, indiquez comment dans les commentaires de cette réponse.
c++
c-preprocessor
Motti
la source
la source
Réponses:
Encapsulent les fonctions de débogage, de passer automatiquement des choses comme
__FILE__
,__LINE__
, etc:la source
__FILE__
et nécessite__LINE__
également le préprocesseur. Les utiliser dans votre code est comme un vecteur d'infection pour le préprocesseur.Les méthodes doivent toujours être du code complet et compilable; les macros peuvent être des fragments de code. Ainsi, vous pouvez définir une macro foreach:
Et utilisez-le comme ceci:
Depuis C ++ 11, ceci est remplacé par la boucle for basée sur la plage .
la source
for_each
c'était une mauvaise chose, car le code par lequel chaque élément était exécuté n'était pas local au point d'appel.foreach
, (et je recommande vivementBOOST_FOREACH
au lieu d'une solution roulée à la main) vous permet de garder le code près du site d'itération, le rendant plus lisible. Cela dit, une fois que le déploiement de lambda sera déployé,for_each
pourrait encore être la voie à suivre.Les gardes de fichiers d'en-tête nécessitent des macros.
Y a-t-il d'autres domaines qui nécessitent des macros? Pas beaucoup (le cas échéant).
Y a-t-il d'autres situations qui bénéficient des macros? OUI!!!
Un endroit où j'utilise des macros est avec du code très répétitif. Par exemple, lors de l'encapsulation de code C ++ à utiliser avec d'autres interfaces (.NET, COM, Python, etc ...), je dois attraper différents types d'exceptions. Voici comment je fais ça:
Je dois mettre ces captures dans chaque fonction de wrapper. Plutôt que de taper les blocs de capture complets à chaque fois, je tape simplement:
Cela facilite également la maintenance. Si jamais je dois ajouter un nouveau type d'exception, je n'ai besoin que d'un seul endroit pour l'ajouter.
Il existe également d'autres exemples utiles: dont beaucoup incluent les macros de préprocesseur
__FILE__
et__LINE__
.Quoi qu'il en soit, les macros sont très utiles lorsqu'elles sont utilisées correctement. Les macros ne sont pas mauvaises - leur mauvaise utilisation est mauvaise.
la source
#pragma once
ces jours-ci, donc je doute que les gardes soient vraiment nécessaires#pragma once
casse sur de nombreux systèmes de construction courants.void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }
. Et du côté des fonctions:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
La plupart:
__LINE__
et__FILE__
)la source
Dans la compilation conditionnelle, pour surmonter les problèmes de différences entre les compilateurs:
la source
close
fonctions ou des méthodes. Ensuite, lorsque vous incluez l'en-tête de cette bibliothèque et l'en-tête avec cette macro que vous avez un gros problème, vous ne pouvez pas utiliser l'API de bibliothèque.#ifdef WE_ARE_ON_WIN32
plz :)Lorsque vous souhaitez créer une chaîne à partir d'une expression, le meilleur exemple est
assert
(#x
transforme la valeur dex
en chaîne).la source
Les constantes de chaîne sont parfois mieux définies en tant que macros, car vous pouvez faire plus avec des littéraux de chaîne qu'avec un
const char *
.Par exemple, les chaînes littérales peuvent être facilement concaténées .
Si a
const char *
était utilisé, une sorte de classe de chaîne devrait être utilisée pour effectuer la concaténation lors de l'exécution:la source
Lorsque vous souhaitez modifier le flux de programme (
return
,break
etcontinue
) le code dans une fonction se comporte différemment du code qui est en fait incorporé dans la fonction.la source
-1
ouNULL
. Une macro peut donc réduire considérablement le code passe-partout.Les évidents incluent les gardes
la source
Vous ne pouvez pas effectuer de court-circuit des arguments d'appel de fonction à l'aide d'un appel de fonction normal. Par exemple:
la source
Disons que nous ignorerons les choses évidentes comme les gardes d'en-tête.
Parfois, vous souhaitez générer du code qui doit être copié / collé par le précompilateur:
ce qui vous permet de coder ceci:
Et peut générer des messages comme:
Notez que mélanger des modèles avec des macros peut conduire à des résultats encore meilleurs (c'est-à-dire générer automatiquement les valeurs côte à côte avec leurs noms de variables)
D'autres fois, vous avez besoin du __FILE__ et / ou du __LINE__ d'un code, pour générer des informations de débogage, par exemple. Ce qui suit est un classique pour Visual C ++:
Comme avec le code suivant:
il génère des messages comme:
D'autres fois, vous devez générer du code en utilisant les opérateurs de concaténation # et ##, comme générer des getters et des setters pour une propriété (c'est pour un cas assez limité, à travers).
D'autres fois, vous générerez du code qui ne sera pas compilé s'il est utilisé via une fonction, comme:
Qui peut être utilisé comme
(encore, je n'ai vu ce genre de code correctement utilisé qu'une seule fois )
Dernier point, mais non le moindre, le fameux
boost::foreach
!!!(Remarque: code copié / collé à partir de la page d'accueil Boost)
Ce qui est (à mon humble avis) bien meilleur que
std::for_each
.Ainsi, les macros sont toujours utiles car elles sont en dehors des règles normales du compilateur. Mais je trouve que la plupart du temps j'en vois un, ce sont en fait des restes de code C jamais traduit en C ++ approprié.
la source
#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; }
cette façon, la macro est beaucoup plus courte.Les frameworks de test unitaire pour C ++ comme UnitTest ++ tournent à peu près autour de macros de préprocesseur. Quelques lignes de code de test unitaire se développent dans une hiérarchie de classes qu'il ne serait pas du tout amusant de taper manuellement. Sans quelque chose comme UnitTest ++ et c'est la magie du préprocesseur, je ne sais pas comment vous pourriez écrire efficacement des tests unitaires pour C ++.
la source
Craindre le préprocesseur C, c'est comme craindre les ampoules à incandescence simplement parce que nous obtenons des ampoules fluorescentes. Oui, le premier peut être {électricité | temps du programmeur} inefficace. Oui, vous pouvez être (littéralement) brûlé par eux. Mais ils peuvent faire le travail si vous le gérez correctement.
Lorsque vous programmez des systèmes embarqués, C est la seule option en dehors de l'assembleur de formulaires. Après avoir programmé sur le bureau avec C ++, puis basculé vers des cibles intégrées plus petites, vous apprenez à cesser de vous soucier des «inélégances» de tant de fonctionnalités C nues (macros incluses) et d'essayer simplement de déterminer la meilleure utilisation sûre que vous pouvez en tirer. fonctionnalités.
Alexander Stepanov dit :
la source
Nous utilisons les macros
__FILE__
et__LINE__
à des fins de diagnostic dans le lancement, la capture et la journalisation d'exceptions riches en informations, ainsi que des analyseurs de fichiers journaux automatisés dans notre infrastructure d'assurance qualité.Par exemple, une macro de lancement
OUR_OWN_THROW
peut être utilisée avec un type d'exception et des paramètres de constructeur pour cette exception, y compris une description textuelle. Comme ça:Cette macro lancera bien sûr l'
InvalidOperationException
exception avec la description comme paramètre du constructeur, mais elle écrira également un message dans un fichier journal composé du nom du fichier et du numéro de ligne où le lancer s'est produit et de sa description textuelle. L'exception levée obtiendra un identifiant, qui sera également enregistré. Si l'exception est déjà interceptée ailleurs dans le code, elle sera marquée comme telle et le fichier journal indiquera alors que cette exception spécifique a été gérée et qu'il est donc peu probable que ce soit la cause d'un crash qui pourrait être connecté plus tard. Les exceptions non gérées peuvent être facilement détectées par notre infrastructure QA automatisée.la source
Répétition du code.
Jetez un oeil pour booster la bibliothèque de préprocesseurs , c'est une sorte de méta-méta-programmation. Dans topic-> motivation, vous pouvez trouver un bon exemple.
la source
Certains éléments très avancés et utiles peuvent encore être construits en utilisant un préprocesseur (macros), ce que vous ne pourriez jamais faire en utilisant les "constructions de langage" c ++, y compris les modèles.
Exemples:
Faire quelque chose à la fois d'un identifiant C et d'une chaîne
Un moyen facile d'utiliser des variables de types enum sous forme de chaîne en C
Booster la métaprogrammation du préprocesseur
la source
stdio.h
etsal.h
enregistrez-vousvc12
pour mieux comprendre.J'utilise occasionnellement des macros pour pouvoir définir des informations en un seul endroit, mais je les utilise de différentes manières dans différentes parties du code. Ce n'est que légèrement mauvais :)
Par exemple, dans "field_list.h":
Ensuite, pour une énumération publique, il peut être défini pour utiliser simplement le nom:
Et dans une fonction d'initialisation privée, tous les champs peuvent être utilisés pour remplir une table avec les données:
la source
Une utilisation courante consiste à détecter l'environnement de compilation.Pour le développement multiplateforme, vous pouvez écrire un ensemble de code pour Linux, par exemple, et un autre pour Windows lorsqu'aucune bibliothèque multiplateforme n'existe déjà pour vos besoins.
Ainsi, dans un exemple approximatif, un mutex multiplateforme peut avoir
Pour les fonctions, elles sont utiles lorsque vous souhaitez ignorer explicitement la sécurité de type. Tels que les nombreux exemples ci-dessus et ci-dessous pour faire ASSERT. Bien sûr, comme beaucoup de fonctionnalités C / C ++, vous pouvez vous tirer une balle dans le pied, mais le langage vous donne les outils et vous permet de décider quoi faire.
la source
Quelque chose comme
Pour que vous puissiez par exemple avoir
et obtenez le nom du fichier source et le numéro de ligne du problème dans votre journal si n est faux.
Si vous utilisez un appel de fonction normal tel que
au lieu de la macro, tout ce que vous pouvez obtenir est le numéro de ligne de votre fonction d'assertion imprimé dans le journal, ce qui serait moins utile.
la source
<cassert>
laassert()
macro, qui vide les informations de fichier / ligne / fonction? (dans toutes les implémentations que j'ai vues, de toute façon)Contrairement à la solution de modèle `` préférée '' présentée dans un thread actuel, vous pouvez l'utiliser comme expression constante:
la source
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Vous pouvez utiliser #defines pour vous aider avec le débogage et les scénarios de test unitaire. Par exemple, créez des variantes de journalisation spéciales des fonctions de mémoire et créez un memlog_preinclude.h spécial:
Compilez votre code en utilisant:
Un lien dans votre memlog.o vers l'image finale. Vous contrôlez maintenant malloc, etc., peut-être à des fins de journalisation, ou pour simuler des échecs d'allocation pour les tests unitaires.
la source
Lorsque vous prenez une décision au moment de la compilation sur le comportement spécifique au compilateur / OS / matériel.
Il vous permet de créer votre interface avec des fonctionnalités spécifiques à Comppiler / OS / Hardware.
la source
J'utilise des macros pour définir facilement les exceptions:
où DEF_EXCEPTION est
la source
Les compilateurs peuvent refuser votre demande de mise en ligne.
Les macros auront toujours leur place.
Quelque chose que je trouve utile est #define DEBUG pour le traçage de débogage - vous pouvez le laisser 1 pendant le débogage d'un problème (ou même le laisser allumé pendant tout le cycle de développement), puis le désactiver quand il est temps d'expédier.
la source
Dans mon dernier emploi, je travaillais sur un antivirus. Pour me faciliter le débogage, j'avais beaucoup de journalisation bloquée partout, mais dans une application à forte demande comme celle-ci, les frais d'un appel de fonction sont tout simplement trop chers. Donc, j'ai créé cette petite macro, qui m'a quand même permis d'activer la journalisation du débogage sur une version commerciale sur un site client, sans le coût d'un appel de fonction, vérifierait l'indicateur de débogage et retournerait sans rien enregistrer, ou si activé , ferait la journalisation ... La macro a été définie comme suit:
En raison de VA_ARGS dans les fonctions de journal, c'était un bon cas pour une macro comme celle-ci.
Avant cela, j'utilisais une macro dans une application de haute sécurité qui devait dire à l'utilisateur qu'il n'avait pas le bon accès, et cela lui disait de quel drapeau il avait besoin.
La ou les macro (s) définies comme:
Ensuite, nous pourrions simplement saupoudrer les vérifications sur toute l'interface utilisateur, et cela vous indiquerait quels rôles étaient autorisés à effectuer l'action que vous avez essayé de faire, si vous ne possédiez pas déjà ce rôle. La raison pour deux d'entre eux était de renvoyer une valeur à certains endroits, et de revenir d'une fonction vide à d'autres ...
Quoi qu'il en soit, c'est comme ça que je les ai utilisés, et je ne sais pas comment cela aurait pu être aidé avec des modèles ... A part ça, j'essaye de les éviter, à moins que VRAIMENT nécessaire.
la source
Encore une autre macros foreach. T: type, c: conteneur, i: itérateur
Utilisation (concept montrant, pas réel):
Meilleures mises en œuvre disponibles: Google "BOOST_FOREACH"
Bons articles disponibles: Amour conditionnel: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html
la source
Peut-être que la meilleure utilisation des macros est dans le développement indépendant de la plate-forme. Pensez aux cas d'incohérence de type - avec les macros, vous pouvez simplement utiliser différents fichiers d'en-tête - comme: --WIN_TYPES.H
--POSIX_TYPES.h
--program.h
Beaucoup plus lisible que de l'implémenter autrement, à mon avis.
la source
Il semble que VA_ARGS n'a été mentionné qu'indirectement jusqu'à présent:
Lorsque vous écrivez du code C ++ 03 générique et que vous avez besoin d'un nombre variable de paramètres (génériques), vous pouvez utiliser une macro au lieu d'un modèle.
Remarque: En général, le nom check / throw pourrait également être incorporé dans la
get_op_from_name
fonction hypothétique . C'est juste un exemple. Il peut y avoir un autre code générique entourant l'appel VA_ARGS.Une fois que nous obtenons des modèles variadiques avec C ++ 11, nous pouvons résoudre cela "correctement" avec un modèle.
la source
Je pense que cette astuce est une utilisation intelligente du préprocesseur qui ne peut pas être émulée avec une fonction:
Ensuite, vous pouvez l'utiliser comme ceci:
Vous pouvez également définir une macro RELEASE_ONLY.
la source
Vous pouvez
#define
utiliser les constantes sur la ligne de commande du compilateur à l'aide de l' option-D
ou/D
. Ceci est souvent utile lors de la compilation croisée du même logiciel pour plusieurs plates-formes, car vous pouvez demander à vos makefiles de contrôler les constantes définies pour chaque plate-forme.la source