10
votes

Code C ++ Pureté

Je travaille dans Enviroment C ++ et:

a) Nous sommes interdits d'utiliser des exceptions
b) Il s'agit d'un code d'application / de serveur de données qui évalue beaucoup de demandes de types différents

J'ai une classification de classe simple résultat de l'opération de serveur qui est également utilisée en interne pour de nombreuses fonctions. P>

#define RETURN_IF_FAILED(call) \
  {                            \
    OpResult result = call;    \
    if (result.failed())       \
      return result;           \
  }


2 commentaires

Et si un certain nettoyage est requis?


Eh bien, ma réponse acceptée a été supprimée unilatéralement par un modérateur aléatoire qui ne l'aime tout simplement pas.


8 Réponses :


9
votes

C'est un compromis. Vous négociez une taille de code pour l'obfuscation de la logique. Je préfère préserver la logique comme visible.

Je n'aime pas les macros de ce type parce qu'ils brisent Intellisense (sous Windows) et débogage de la logique du programme. Essayez de mettre un point d'arrêt sur les énoncés 10 Retour dans votre fonction - non pas le chèque, juste le retour . Essayez de passer à travers le code qui est dans la macro.

La pire chose à ce sujet est qu'une fois que vous avez accepté cela, il est difficile de discuter contre les macros monstres de 30 lignes que certains programmeurs aiment utiliser pour des mini-tâches communément observées, car elles «clarifient les choses». J'ai vu du code dans lequel différents types d'exception ont été traités de cette manière par quatre macros en cascade, ce qui entraîne 4 lignes dans le fichier source, avec les macros en expansion pour> 100 lignes réelles. Maintenant, vous réduisez votre code de code? Non, il est impossible de raconter facilement avec des macros.

Un autre argument général contre les macros, même s'il n'est pas manifestement applicable ici, est la capacité de les nichier de manière difficile à déchiffrer des résultats ou de transmettre des arguments qui entraînent des arguments étranges mais compilables. L'utilisation de ++ x dans une macros qui utilise l'argument deux fois. Je sais toujours où je me tiens au code et je ne peux pas dire ça sur une macro.

Edit: Un commentaire Je devrais ajouter est que si vous répétez vraiment cette erreur, la logique de vérification des erreurs, peut-être, il existe peut-être des opportunités de refactorisation dans le code. Pas une garantie, mais une meilleure façon de réduire de code de codes si elle s'applique. Recherchez des séquences répétées d'appels et encapsulez des séquences communes dans leur propre fonction, plutôt que de répondre à la manière dont chaque appel est manipulé isolément.


3 commentaires

Je conviens que la macro devrait toujours être la dernière chose à utiliser, lorsque tous les autres moyens de «clarifier les choses» ont été jugés, et cela devrait être simple, mais c'est vraiment préférable de copyplasta la macro TheWher au lieu de son utilisation?


Qu'il augmente le code de code de code ou ne dépend pas de savoir s'il existe ou non une alternative à ces macros. Si l'alternative aux macros consiste à écrire manuellement les 100 lignes, les macros ne augmentent pas le code de code. S'il y a une alternative, les macros sont discutables.


@Peter - En désaccord avec respect. Je ne mettrais jamais que de nombreuses lignes de code dans une macro pour le code de production. Le code de test est une autre chose. Il est littéralement impossible de déboguer ce code et lorsque la vraie nature du code est cachée à une telle degré, c'est une odeur de code quel que soit le mécanisme d'obscurcissement.



5
votes

En fait, je préfère légèrement autre solution. La chose est que le résultat de l'appel intérieur n'est pas nécessairement un résultat valide d'un appel externe. Par exemple, une défaillance interne peut être "Fichier introuvable", mais la "configuration non disponible" extérieure ". Par conséquent, ma suggestion est de recréer le opresult (potentiellement emballer l'opresulte "intérieure" pour obtenir un meilleur débogage). Tout cela va à la direction de "InnErException" dans .NET.

Techniquement, dans mon cas, la macro ressemble à xxx

cette solution nécessite toutefois une gestion de la mémoire, etc.

Un certain puriste affirmant qu'aucun rendement explicite entrave la lisibilité du code. À mon avis, avoir explicitement retour dans la partie du nom de la macro suffit à empêcher la confusion pour tout développeur qualifié et attentionné.


Mon avis est que de telles macros Don 't obscurcir la logique du programme, mais au contraire le rendre plus propre. Avec une telle macro, vous déclarez votre intention de manière claire et concise, tandis que l'inverse de l'autre sens semble être trop verbeuse et donc une erreur d'erreur. Faire l'analyse des Mainteniers d'analyser la même construction opresult r = appel (); Si (R.Failed) Retour R Gasturez-vous de leur temps.

Une approche alternative sans retour précoce s'applique à chaque ligne de code Le motif de type CochedCall (r, appel) < / code> avec #define checkedCall (r, appelez) do {if (r.sucédé) r = appel; } tandis que (faux) . Ceci est dans mes yeux beaucoup moins pire et définitivement surmonté, car les gens ont tendance à oublier l'ajout de checkedCall () lors de l'ajout de plus de code.

ayant un Popular < / em> besoin de faire vérifier les retours (ou tout) avec des macros semble être un léger signe de fonctionnalité de langue manquante pour moi.


3 commentaires

Quelque chose de similaire pourrait être aussi utile.


Si vous en avez besoin, mettez ces appels internes dans leurs propres fonctions (inlined) avec une fonction extérieure interprétant leurs résultats. Ensuite, il n'y a pas besoin de ce piratage macro.


@SBI: Mon code dans la réponse est juste un indice. Le code de production réel utilise certaines fonctions d'assistance appropriées.



2
votes

Tant que la définition macro est assise dans un fichier de mise en œuvre et n'est pas défini dès que cela n'est pas nécessaire, je ne serais pas horrifié . XXX

Cependant, je n'utiliserait que cela après avoir exclu toutes les solutions non macro.


0 commentaires

0
votes

Je suis d'accord avec Steve's POV .

Je pensais d'abord, au moins réduisez le macro à xxx

mais il est arrivé à moi déjà est un one-liner, donc Il y a vraiment peu d'avantages dans la macro.


Je pense, essentiellement, vous devez faire un compromis entre la capacité d'écriture et la lisibilité. La macro est définitivement plus facile à écrire . C'est cependant une question ouverte si elle est également plus facile à lue . Ce dernier est un jugement subjectif à faire. Néanmoins, en utilisant des macros objectivement les obfusque code.


En fin de compte, le problème sous-jacent est que vous ne devez pas utiliser des exceptions. Vous n'avez pas dit quelles sont les raisons de cette décision, mais j'espère sûrement qu'ils valent les problèmes de cette cause.


4 commentaires

De cette façon, vous devez appeler la fonction, puis la macro, ainsi que vous devez également utiliser un bloc partout, ou être prudent pour ne pas faire de variables de résultat en double.


La macro de votre exemple n'est pas optimale, car elle apporterait une erreur dans le code comme si (cond) retour_if_failed (...) sinon do_something_else; . Vous voudriez peut-être ajouter l'habituel do {...} alors que (0) autour de.


@Vlad: Je discutais contre cette macro, C'est pourquoi je n'ai pas réfléchi.


@Marwin: Vous n'avez pas besoin d'être des précautions, car le compilateur l'attrapera si vous échouez.



0
votes

pourrait être fait avec C ++ 0x Lambdas.

template<typename F> inline OpResult if_failed(OpResult a, F f) {
    if (a.failed())
        return a;
    else
        return f();
};

OpResult something() {
    int mah_var = 0;
    OpResult x = do_something();
    return if_failed(x, [&]() -> OpResult {
        std::cout << mah_var;
        return f;
    });
};


0 commentaires

0
votes

À mon avis, cachez une déclaration de retour dans une macro est une mauvaise idée. L'obfiducation de code '(j'aime ce terme ..!) Atteint le niveau le plus élevé possible. Ma solution habituelle à ces problèmes est d'agréger l'exécution de la fonction à un endroit et de contrôler le résultat de la manière suivante (en supposant que vous disposez de 5 fonctions nullaires):

std::array<std::function<OpResult ()>, 5>  tFunctions = {
 f1, f2, f3, f4, f5
};

auto tFirstFailed = std::find_if(tFunctions.begin(), tFunctions.end(), 
    [] (std::function<OpResult ()>& pFunc) -> bool {
        return pFunc().failed();
    });

if (tFirstFailed != tFunctions.end()) {
 // tFirstFailed is the first function which failed...
}


1 commentaires

Il est difficile d'appeler cette cachette une déclaration de retour, lorsque le nom de la macro commence par retour_if _...



0
votes

Y a-t-il des informations en résultat qui est en fait utile si l'appel échoue?

sinon, alors xxx

suffirait.


0 commentaires

0
votes

Après 10 ans, je vais répondre à ma propre question à ma satisfaction, si seulement j'avais une machine à remous ...

J'ai rencontré plusieurs fois de nombreuses reprises dans de nouveaux projets. Même lorsque des exceptions ont été autorisées, je ne veux pas toujours les utiliser pour "Échec normal". P>

J'ai finalement découvert un moyen d'écrire ce genre de déclarations. P>

pour le résultat générique Cela inclut le message, j'utilise ceci: p> xxx pré>

et ensuite, j'ai une classe d'assistance spéciale dans ce formulaire (similaire pour d'autres types de retour) p>

if (Failed result = trySomething())
  showError(result.getMessage().str());


0 commentaires