Déjà pour l'histoire des macros, évidemment, ce sont de très mauvaises macro à éviter absolument
Comme la plupart des macros, en fait.
Dès lors que le bloc "macroifié" n'utilise pas les éléments de syntaxe du pré-processeur (concaténation, stringification, __DATE__, __LINE__, __FILE__,...) alors il vaut souvent mieux utiliser des fonctions, quitte a les mettre dans le header avec un inline bien senti.
Plus facile a aligner, pas besoin de s'emm... avec les continuations de ligne (parce que #define ne travaille que sur une ligne, donc il faut \\ le retour chariot OU faire un one-liner. Dans les deux cas, la lisibilité en prend un coup), se base sur le compilateur qui est plus intelligent que le pré-processeur (type-safety, notamment), isolation des variables (une macro ne limite pas la portée, une fonction si) qui implique qu'on risque moins de "shadow" autre chose par accident parce qu'on a oublié d'utiliser le workaround do { ... } while (false) (ou qu'on a malencontreusement ajouté un ; à la fin, même si ça, au moins, ne générera pas de bugs a ma connaissance)...
Et pour ce que tu appelles la programmation défensive (bizarre comme terme, ça existe la programmation offensive où on cherchce volontairement à se mettre dans des situations compliquées ???)
Je confirme que ça existe, j'en ai entendu parler à plus d'un endroit. Le concept m'a toujours semblé un peu flou, ceci dit.
Parce que bon, moi aussi, je teste le retour d'erreur de la plupart de mes fonctions... (pas toutes, j'ai souvent la flemme avec printf(), par exemple) et je n'ai pas l'impression d'être spécialement défensif: c'est surtout histoire d'avoir des logs utilisables quand ça va bugguer (parce que ça finit toujours par bugguer), plutôt que de devoir galérer a trouver une repro au pifomètre... puis un log bien fait, c'est quand même plus simple à lire qu'un coredump...
Je suis aussi d'accord sur la remise à zéro du pointeur. C'est un truc qu'il m'arrive de faire, notamment quand j'ai affaire a des conteneurs "fait main": je préfère une RàZ inutile plutôt que passer des heures a trouver d'où viens un bug, tout ça pour m'apercevoir que le pointeur est libéré 2 fois, utilisé alors qu'il devrait être invalide, ou ce genre de joyeusetés... C'est particulièrement pertinent en C, d'ailleurs, qui n'offre aucun moyen de gérer ce genre de besoins basiques automatiquement. Un jour, peut-être, il sera possible d'écrire du C sans se farcir ces infâmes piles de labels pour la gestion d'échec. Mais d'ici la, je continuerai a considérer le C comme un artefact du passé.
Je préfère perdre quelques cycles à xor un emplacement mémoire, que des heures a débuguer un truc dont la reproduction est aléatoire (autant j'aime les grosses segfault qui tâchent, facile a choper, vous savez, celles qu'on reproduit trivialement... autant les autres types de corruption mémoire j'aime pas!).
[^] # Re: Cas plus concrets
Posté par freem . En réponse au journal Traduction | Doit-on vérifier le pointeur pour NULL avant d'appeler la fonction free ?. Évalué à 3.
Comme la plupart des macros, en fait.
Dès lors que le bloc "macroifié" n'utilise pas les éléments de syntaxe du pré-processeur (concaténation, stringification,
__DATE__,__LINE__,__FILE__,...) alors il vaut souvent mieux utiliser des fonctions, quitte a les mettre dans le header avec un inline bien senti.Plus facile a aligner, pas besoin de s'emm... avec les continuations de ligne (parce que
#definene travaille que sur une ligne, donc il faut\\le retour chariot OU faire un one-liner. Dans les deux cas, la lisibilité en prend un coup), se base sur le compilateur qui est plus intelligent que le pré-processeur (type-safety, notamment), isolation des variables (une macro ne limite pas la portée, une fonction si) qui implique qu'on risque moins de "shadow" autre chose par accident parce qu'on a oublié d'utiliser le workarounddo { ... } while (false)(ou qu'on a malencontreusement ajouté un;à la fin, même si ça, au moins, ne générera pas de bugs a ma connaissance)...Je confirme que ça existe, j'en ai entendu parler à plus d'un endroit. Le concept m'a toujours semblé un peu flou, ceci dit.
Parce que bon, moi aussi, je teste le retour d'erreur de la plupart de mes fonctions... (pas toutes, j'ai souvent la flemme avec printf(), par exemple) et je n'ai pas l'impression d'être spécialement défensif: c'est surtout histoire d'avoir des logs utilisables quand ça va bugguer (parce que ça finit toujours par bugguer), plutôt que de devoir galérer a trouver une repro au pifomètre... puis un log bien fait, c'est quand même plus simple à lire qu'un coredump...
Je suis aussi d'accord sur la remise à zéro du pointeur. C'est un truc qu'il m'arrive de faire, notamment quand j'ai affaire a des conteneurs "fait main": je préfère une RàZ inutile plutôt que passer des heures a trouver d'où viens un bug, tout ça pour m'apercevoir que le pointeur est libéré 2 fois, utilisé alors qu'il devrait être invalide, ou ce genre de joyeusetés... C'est particulièrement pertinent en C, d'ailleurs, qui n'offre aucun moyen de gérer ce genre de besoins basiques automatiquement. Un jour, peut-être, il sera possible d'écrire du C sans se farcir ces infâmes piles de labels pour la gestion d'échec. Mais d'ici la, je continuerai a considérer le C comme un artefact du passé.
Je préfère perdre quelques cycles à xor un emplacement mémoire, que des heures a débuguer un truc dont la reproduction est aléatoire (autant j'aime les grosses segfault qui tâchent, facile a choper, vous savez, celles qu'on reproduit trivialement... autant les autres types de corruption mémoire j'aime pas!).