2010-06-23 14 views
10

Je voudrais écrire une routine comme printf, pas fonctionnellement, mais j'aimerais que la routine compile en même temps les caractéristiques de vérification de printf.Comment obtenir des avertissements ou des erreurs de compilation au format printf

Par exemple, si j'ai:

{ 
    int i; 
    std::string s; 
    printf("%d %d",i); 
    printf("%d",s.c_str()); 
} 

Le compilateur se plaint comme ceci:

1 cc1plus: warnings being treated as errors 
2 In function 'int main()': 
3 Line 8: warning: too few arguments for format 
4 Line 9: warning: format '%d' expects type 'int', but argument 2 has type 'const char*' 

code example

sont des fonctions printf et co spéciales que les compilateur traite différemment ou est-il un peu astuce pour obtenir ce travail sur toute fonction définie par l'utilisateur? Les compilateurs spécifiques qui m'intéressent sont gcc et msvc

Répondre

17

Différents compilateurs peuvent implémenter cette fonctionnalité différemment. Dans GCC, il est implémenté par l'intermédiaire du spécificateur __attribute__ avec l'attribut format (lire à ce sujet here). La raison pour laquelle le compilateur effectue la vérification est juste que dans les fichiers d'en-tête standard fournis avec GCC la fonction printf est déclarée avec __attribute__((format(printf, 1, 2)))

exactement de la même façon que vous pouvez utiliser format attribut pour étendre les mêmes fonctionnalités de vérification format à votre propres fonctions variadiques qui utilisent les mêmes spécificateurs de format que printf. Cela fonctionnera uniquement si la convention de passage de paramètre et les spécificateurs de format que vous utilisez sont les mêmes que ceux utilisés par les fonctions printf et scanf standard. Les vérifications sont codées en dur dans le compilateur. Si vous utilisez une convention différente pour le passage d'arguments variadiques, le compilateur ne vous aidera pas à le vérifier.

+4

Il peut faire un peu plus de printf et scanf; la liste des docs actuels est "printf, scanf, strftime, gnu_printf, gnu_scanf, gnu_strftime ou strfmon" – Cascabel

+2

C'est cool. J'espère que CodeGear/Embarcadero mettra cette fonctionnalité dans leur compilateur dans le futur. –

3

printf() et les amis ne sont pas spéciaux car ils acceptent un nombre variable d'arguments: les fonctions définies par l'utilisateur peuvent aussi accepter un nombre variable d'arguments. Ils sont spéciaux parce que leur comportement est défini par la norme, de sorte que le compilateur sait ce que la corrélation devrait être entre la chaîne de format et les arguments passés à la fonction. Effectivement, le compilateur sait combien d'arguments sont passés à la fonction quand il est appelé, donc il analyse la chaîne de format et compare le nombre attendu et les types d'arguments avec les arguments réellement passés à la fonction et émet un avertissement si elles ne correspondent pas.

Si vous utilisez C++, j'éviterais d'écrire vos propres fonctions variadiques; il y a peu de bonnes raisons de les utiliser dans la plupart des projets. Par exemple, si vous faites du formatage, utilisez des flux ou une bibliothèque comme Boost Format. Tout problème qui peut être résolu en utilisant une fonction variadique peut être résolu en utilisant une fonction non-variadique, et dans presque tous les cas, le résultat est plus élégant, idiomatique et sans danger pour le type.

+4

Il y a en fait beaucoup de bonnes raisons pour éviter les flux et Boost quand on est en train de coder en C++, mais tous sont spécifiques au domaine. La plupart d'entre nous font un certain nombre de travaux spécifiques au domaine, donc nous devrions essayer de ne pas perdre de vue le fait qu'il existe un nombre important de personnes pour lesquelles de nombreuses fonctionnalités C++ sont trop chères. –

+1

@ dash-tom-bang: Oui, vous avez raison: il y a certainement des scénarios dans lesquels ils sont utiles. –

1

En fait, printf ne possède aucune sécurité de compilation inhérente. Il se trouve que certains compilateurs plus récents ont mis en place des vérifications spéciales étant donné qu'ils savent exactement ce que signifie une chaîne de format en termes de paramètres supplémentaires. Lorsque vous utilisez ... comme paramètre, vous dites que vous voulez accepter des arguments arbitraires et acceptez l'entière responsabilité pour vous assurer qu'ils sont corrects. Le compilateur n'a aucun moyen de vérifier la sécurité du nombre/type. Au lieu d'essayer de faire en sorte que le compilateur vous aide de cette manière, essayez d'utiliser l'approche utilisée par les flux standards: Utilisez une fonction (éventuellement un modèle) ou un opérateur qui renvoie une référence à this pour autoriser le chaînage. Ensuite, le compilateur sera capable de vous dire tout de suite quand les arguments ne correspondent pas à ce qui est attendu/supporté.

1

Il y a quelque temps, quelqu'un a posté un mpl :: string dans les groupes boost. Je pense que ça a peut-être été dans la bibliothèque. Si tel est le cas, vous pouvez implémenter quelque chose comme ceci en fournissant votre chaîne de template en tant que paramètre template (un mpl :: string) et en utilisant des compétences de méta-programmation assez profondes pour analyser les bits de formatage. Ensuite, vous utiliserez cette information pour choisir une implémentation qui a le nombre d'arguments et les types appropriés.

Non, je ne vais pas le faire pour vous: P Ce serait très difficile. Cependant, je crois que ce serait possible.