J'utilise beaucoup de printf
fins de traçage / journalisation dans mon code, j'ai trouvé que c'est une source d'erreur de programmation. J'ai toujours trouvé que l'opérateur d'insertion ( <<
) était quelque chose d'étrange, mais je commence à penser qu'en l'utilisant à la place, je pourrais éviter certains de ces bogues.
Quelqu'un a déjà eu une révélation similaire ou je saisis juste des pailles ici?
Certains emportent des points
- Ma pensée actuelle est que la sécurité de type l'emporte sur tout avantage d'utiliser printf. Le vrai problème est la chaîne de formatage et l'utilisation de fonctions variadiques non sécurisées.
- Peut-être que je n'utiliserai pas
<<
les variantes du flux de sortie stl mais je vais certainement chercher à utiliser un mécanisme de type sûr qui est très similaire. - Une grande partie du traçage / journalisation est conditionnelle, mais je voudrais toujours exécuter le code pour ne pas manquer les bogues dans les tests simplement parce que c'est une branche rarement prise.
printf
dans le monde C ++? Il me manque quelque chose ici?printf
en C ++. (Que ce soit une bonne idée est une autre question.)printf
a quelques avantages; voir ma réponse.Réponses:
printf, en particulier dans les cas où vous pourriez vous soucier des performances (comme sprintf et fprintf) est un hack vraiment étrange. Cela m'étonne constamment que les gens qui martèlent le C ++ en raison de la surcharge de performance minuscule liée aux fonctions virtuelles continueront ensuite à défendre l'io de C.
Oui, afin de comprendre le format de notre sortie, quelque chose que nous pouvons savoir à 100% au moment de la compilation, analysons une chaîne de format Fricken au moment de l'exécution dans une table de saut massivement bizarre en utilisant des codes de format impénétrables!
Bien sûr, ces codes de format ne pouvaient pas être faits pour correspondre aux types qu'ils représentent, ce serait trop facile ... et vous êtes rappelé à chaque fois que vous recherchez si c'est% llg ou% lg que cette langue (fortement typée) vous rend déterminer manuellement les types pour imprimer / numériser quelque chose, ET a été conçu pour les processeurs pré-32 bits.
J'admets que la gestion de la largeur et de la précision du format par C ++ est volumineuse et pourrait utiliser du sucre syntaxique, mais cela ne signifie pas que vous devez défendre le hack bizarre qui est le principal système io de C. Les bases absolues sont assez faciles dans les deux langues (bien que vous devriez probablement utiliser quelque chose comme une fonction d'erreur personnalisée / un flux d'erreur pour le code de débogage), les cas modérés sont de type regex en C (faciles à écrire, difficiles à analyser / déboguer ), et les cas complexes impossibles en C.
(Si vous utilisez des conteneurs standard, écrivez-vous des surcharges d'opérateur rapide << qui vous permettent de faire des choses comme
std::cout << my_list << "\n";
pour le débogage, où ma_liste est de typelist<vector<pair<int,string> > >
.)la source
operator<<(ostream&, T)
en appelant ... enfinsprintf
,! Les performances desprintf
ne sont pas optimales, mais de ce fait, les performances des iostreams sont généralement encore pires.Mélanger une sortie de style C
printf()
(ouputs()
ouputchar()
ou ...) avec une sortie de style C ++std::cout << ...
peut être dangereux. Si je me souviens bien, ils peuvent avoir des mécanismes de mise en mémoire tampon distincts, de sorte que la sortie peut ne pas apparaître dans l'ordre prévu. (Comme AProgrammer le mentionne dans un commentaire,sync_with_stdio
traite de cela).printf()
est fondamentalement dangereux pour les types. Le type attendu pour un argument est déterminé par la chaîne de format ("%d"
nécessite unint
ou quelque chose qui promeutint
,"%s"
nécessite unchar*
qui doit pointer vers une chaîne de style C correctement terminée, etc.), mais le fait de passer le mauvais type d'argument entraîne un comportement indéfini , pas une erreur diagnostiquable. Certains compilateurs, tels que gcc, font un assez bon travail d'avertissement sur les incompatibilités de type, mais ils ne peuvent le faire que si la chaîne de format est un littéral ou est autrement connue au moment de la compilation (ce qui est le cas le plus courant) - et tel les avertissements ne sont pas requis par la langue. Si vous passez le mauvais type d'argument, des choses arbitrairement mauvaises peuvent se produire.Les E / S de flux de C ++, en revanche, sont beaucoup plus sécuritaires
<<
pour les types , car l' opérateur est surchargé pour de nombreux types différents.std::cout << x
n'a pas besoin de spécifier le type dex
; le compilateur générera le bon code pour tout type dex
fichier.D'un autre côté,
printf
les options de formatage de sont à mon humble avis beaucoup plus pratiques. Si je veux imprimer une valeur à virgule flottante avec 3 chiffres après la virgule décimale, je peux l'utiliser"%.3f"
- et cela n'a aucun effet sur les autres arguments, même dans le mêmeprintf
appel. Les C ++setprecision
, d'autre part, affectent l'état du flux et peuvent gâcher la sortie ultérieure si vous ne faites pas très attention à restaurer le flux à son état précédent. (Ceci est ma bête noire personnelle; si je manque un moyen propre de l'éviter, veuillez commenter.)Les deux ont des avantages et des inconvénients. La disponibilité de
printf
est particulièrement utile si vous avez un arrière-plan C et que vous le connaissez mieux, ou si vous importez du code source C dans un programme C ++.std::cout << ...
est plus idiomatique pour C ++, et n'exige pas autant de soin pour éviter les décalages de type. Les deux sont valides C ++ (la norme C ++ inclut la plupart de la bibliothèque standard C par référence).Il est probablement préférable de l'utiliser
std::cout << ...
pour le plaisir d'autres programmeurs C ++ qui peuvent travailler sur votre code, mais vous pouvez utiliser l'un ou l'autre - en particulier dans le code de trace que vous allez jeter.Et bien sûr, cela vaut la peine de passer du temps à apprendre à utiliser les débogueurs (mais cela pourrait ne pas être possible dans certains environnements).
la source
printf
.std::ios_base::sync_with_stdio
.std::cout
utilise un appel distinct pour chaque élément imprimé? Vous pouvez contourner cela en collectant une ligne de sortie dans une chaîne avant de l'imprimer. Et bien sûr, vous pouvez également imprimer un élément à la fois avecprintf
; il est juste plus pratique d'imprimer une ligne (ou plus) en un seul appel.Votre problème vient très probablement du mélange de deux gestionnaires de sortie standard très différents, chacun ayant son propre programme pour ce pauvre petit STDOUT. Vous obtenez aucune garantie sur la façon dont ils sont mis en œuvre, et il est tout à fait possible qu'ils mettent en conflit d' options de descripteurs de fichiers, à la fois essayer de faire des choses différentes à elle, etc. En outre, les opérateurs d'insertion ont une importante plus
printf
:printf
vous permettra de faire ceci:Alors que
<<
non.Remarque: pour le débogage, vous n'utilisez pas
printf
oucout
. Vous utilisezfprintf(stderr, ...)
etcerr
.la source
printf
type n'est pas sûr et je pense que la sécurité du type l'emporte sur tout avantage de l'utilisationprintf
. Le problème est vraiment la chaîne de format et la fonction variadique non sécurisée.SomeObject
n'était pas un pointeur? Vous allez obtenir des données binaires arbitraires que le compilateur décide de représenterSomeObject
.Il existe de nombreux groupes - par exemple Google - qui n'aiment pas les flux.
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams
(Ouvrez le triangle pour que vous puissiez voir la discussion.) Je pense que le guide de style google C ++ a BEAUCOUP de conseils très judicieux.
Je pense que le compromis est que les flux sont plus sûrs mais printf est plus clair à lire (et plus facile d'obtenir exactement le formatage que vous souhaitez).
la source
printf
peut provoquer des bogues en raison du manque de sécurité de type. Il existe plusieurs façons de résoudre ce problème sans passer àiostream
l'<<
opérateur de et à un formatage plus compliqué:printf
chaînes de format par rapport auxprintf
arguments et peuvent afficher des avertissements tels que les suivants s'ils ne correspondent pas.printf
appels -style pour les rendre sûrs pour le type.printf
chaînes de format de type similaire (les Boost.Format en particulier sont presque identiques àprintf
) tout en conservantiostreams
la sécurité et l'extensibilité du type.la source
La syntaxe de Printf est fondamentalement correcte, moins une saisie obscure. Si vous pensez que c'est mal pourquoi C #, Python et d'autres langages utilisent la construction très similaire? Le problème en C ou C ++: il ne fait pas partie d'un langage et n'est donc pas vérifié par le compilateur pour la syntaxe correcte (*) et n'est pas décomposé en série d'appels natifs si l'optimisation de la vitesse. Notez que si vous optimisez la taille, les appels printf pourraient s'avérer plus efficaces! La syntaxe de streaming C ++ est à mon humble avis tout sauf bonne. Cela fonctionne, la sécurité de type est là, mais la syntaxe verbeuse ... blague. Je veux dire que je l'utilise, mais sans joie.
(*) certains compilateurs font cette vérification ainsi que presque tous les outils d'analyse statique (j'utilise Lint et je n'ai jamais eu de problème avec printf depuis).
la source
format("fmt") % arg1 % arg2 ...;
) avec la sécurité de type. Au prix de quelques performances supplémentaires, car il génère des appels de chaîne de caractères qui génèrent en interne des appels sprintf dans de nombreuses implémentations.printf
est, à mon avis, un outil de sortie beaucoup plus flexible pour traiter les variables que n'importe quelle sortie de flux CPP. Par exemple:Cependant, lorsque vous voudrez peut-être utiliser l'
<<
opérateur CPP , c'est lorsque vous le surchargez pour une méthode particulière ... par exemple pour obtenir le vidage d'un objet qui contient les données d'une personne particulière,PersonData
....Pour cela, il serait beaucoup plus efficace de dire (en supposant qu'il
a
s'agit d'un objet de PersonData)que:
La première est beaucoup plus conforme au principe d'encapsulation (pas besoin de connaître les spécificités, les variables de membre privé), et est également plus facile à lire.
la source
Vous n'êtes pas censé utiliser
printf
en C ++. Déjà. La raison est, comme vous l'avez correctement noté, que c'est une source de bogues et le fait que l'impression de types personnalisés, et en C ++ presque tout devrait être des types personnalisés, est pénible. La solution C ++ est les flux.Cependant, il existe un problème critique qui rend les flux inappropriés pour toute sortie visible par l'utilisateur! Le problème, ce sont les traductions. Un exemple d'emprunt du manuel gettext dit que vous voulez écrire:
Maintenant, le traducteur allemand arrive et dit: Ok, en allemand, le message devrait être
Et maintenant vous avez des ennuis, car il a besoin que les morceaux soient mélangés. Il faut dire que même de nombreuses mises en œuvre
printf
ont des problèmes avec cela. À moins qu'ils ne prennent en charge l'extension afin que vous puissiez utiliserLe Boost.Format prend en charge les formats de style printf et dispose de cette fonctionnalité. Vous écrivez donc:
Malheureusement, cela entraîne un peu de pénalité en termes de performances, car en interne, il crée un flux de chaînes et utilise l'
<<
opérateur pour formater chaque bit et dans de nombreuses implémentations, l'<<
opérateur appelle en internesprintf
. Je soupçonne une mise en œuvre plus efficace serait possible si vraiment souhaité.la source
Vous faites beaucoup de travail inutile, à côté du fait que
stl
c'est mauvais ou non, déboguez votre code avec une série deprintf
seulement ajouter 1 niveau supplémentaire d'échecs possibles.Utilisez simplement un débogueur et lisez quelque chose sur les exceptions et comment les attraper et les lancer; essayez de ne pas être plus verbeux que ce dont vous avez réellement besoin.
PS
printf
est utilisé en C, pour le C ++ que vous avezstd::cout
la source