2

Je travaille sur une très grande base de code (sur 3M loc) nous avons évidemment beaucoup de classes mais la plupart d'entre elles n'utilisent pas de listes d'initialisation dans leurs constructeurs, mais assignent les valeurs dans le corps du constructeur (une partie du code a été écrit il y a longtemps, donc c'est devenu un standard de facto). Peut-être que ceux-ci sont optimisés par le compilateur, mais je ne suis pas sûr que ce soit vraiment le cas. J'essaie de promouvoir l'utilisation des listes d'initialisation, mais il y a une base de code importante qui nécessite d'être mise à jour, donc y a-t-il des outils qui vont le faire pour moi automatiquement? Pointez-le dans une classe, trouvez toutes les lignes m_var = 0; et déplacez-les dans la liste d'initialisation (en le créant si nécessaire).Constructeur Refactor pour utiliser la liste d'initialisation

Outre la conversion de l'initialisation du corps en listes d'initialisation, existe-t-il un moyen de vérifier que les variables membres sont initialisées dans le bon ordre (par exemple dans le même ordre que le fichier d'en-tête de la classe? espérait que cppCheck choisirait ça, mais il ne semble pas.

+1

Comme pour la deuxième partie: GCC a '-Wreorder'. – Biffen

+0

@Biffen fait Visual Studio 2012? Comme c'est ce que j'utilise. – fwgx

+1

Pas que je sache, et un google rapide n'a rien donné. Je vais vous laisser faire plus de recherches à ce sujet. – Biffen

Répondre

2

Bonjour Je suis un développeur de cppcheck.

cppcheck dispose également d'un chèque de l'ordre désadaptation. mais il est un contrôle non concluant.

Par exemple:

class Fred { 
public: 
    Fred() : y(0), x(0) {} 
    int x; 
    int y; 
}; 

sortie Cppcheck:

[email protected]:~/cppcheck$ ./cppcheck --enable=style --inconclusive 1.cpp 
Checking 1.cpp ... 
[1.cpp:3] -> [1.cpp:4]: (style, inconclusive) Member variable 'Fred::x' is in the wrong place in the initializer list. 

Notre simple vérification va juste avertir chaque fois que le décalage de commande. C'est pourquoi ce n'est pas concluant. Dans le code ci-dessus, l'ordre d'initialisation n'a pas vraiment d'importance - puisque tous les membres du code ci-dessus sont ints et tous les initialiseurs sont des littéraux constants.

1

Avec une échelle comme la taille de la base de code de l'OP, ce qui est nécessaire est un program transformation system (PTS). C'est un outil qui analyse un fichier source de langage cible dans des structures de données de compilateur (habituellement des AST), vous permet d'appliquer des transformations aux ASTs, puis de régénérer un code source valide incluant les commentaires originaux pour le programme modifié. Pensez à PST comme des outils pour refactoring dans le grand.

Un bon PTS vous permettra d'écrire source-source transforme de la forme:

when you see *this*, replace it by *that* if *condition* 

ce et que sont exprimés dans la syntaxe de la langue cible, où ce correspond uniquement si le code source correspond à la syntaxe explicite. [Ce ne sont pas des correspondances de chaînes; ils travaillent sur les AST, donc la mise en page n'affecte pas leur capacité à correspondre].

Vous avez besoin d'une règle clé qui ressemble à ceci:

rule move_to_initializer(constructor_name:IDENTIFIER, 
         arguments: argument_list, 
         initializer_list: initializer, 
         member_name:IDENTIFIER, 
         initializer_expression: expression, 
         statements: statement_list 
         ): constructor -> constructor = 
    " \constructor_name(\arguments): \initializer_list 
      { \member_name = \initializer_expression ; 
      \statements } " 
    -> " \constructor_name(\arguments): \initializer_list, \member_name(\initializer_expression) 
      { \statements } "; 

The syntax of these rules/patterns for our DMS Software Reengineering Toolkit is explained here. DMS est le seul PTS source-à-source que je connaisse capable de gérer C++; il gère même le dialecte MSVS].

J'ai omis une éventuelle "if condition" vérifier que le nom de membre est bien un membre de la classe, en supposant que vos constructeurs ne sont pas abusifs.

Parce que vos constructeurs ne peuvent pas avoir une liste d'initialisation, vous avez besoin d'une règle d'aide pour introduire un si nécessaire:

rule move_to_initializer(constructor_name:IDENTIFIER, 
         arguments: argument_list, 
         member_name:IDENTIFIER, 
         initializer_expression: expression, 
         statements: statement_list 
         ): constructor -> constructor = 
    " \constructor_name(\arguments) 
      { \member_name = \initializer_expression ; 
      \statements } " 
    -> " \constructor_name(\arguments): \member_name(\initializer_expression) 
      { \statements } "; 

      { \member_name = \e ; } " 

Invariablement vous avez besoin des règles supplémentaires pour couvrir d'autres cas particuliers, mais il ne devrait pas être plus quelques.

En ce qui concerne le contrôle sur d'ordre de l'initialisation, vous pouvez déclencher un tel contrôle à l'aide d'un (DMS) modèle:

pattern check_initializer_order(constructor_name:IDENTIFIER, 
         initializer_list: initializer, 
         statements: statement_list 
         ): constructor = 
    " \constructor_name(): \initializer_list, 
      { \statements } " 
      if complain_if_not_ordered(constructor_name,initializer_list); 

qui nécessite une méta-prédicat auxiliaire qui vérifie l'ordre, qui se plaint si elles sont mal coordonné. Vous avez besoin de constructor_name pour permettre au prédicat de rechercher la classe correspondante et d'inspecter l'ordre des membres. [DMS fournit le means to access a symbol table avec cette information].

Alternativement, vous pouvez tout simplement réordonner les utiliser une règle de réécriture différente:

rule order_initializers(constructor_name:IDENTIFIER, 
         arguments: argument_list, 
         initializer_list_prefix: initializer, 
         initializer_list_suffix: initializer, 
         member1_name:IDENTIFIER, 
         initializer1_expression: expression, 
         member2_name:IDENTIFIER, 
         initializer2_expression:expression,        
         statements: statement_list 
         ): constructor -> constructor = 
    " \constructor_name(\arguments): 
      \initializer_list_prefix, 
      \member1_name(\initializer1), 
      \member2_name(\initializer2), 
      \initialize_list_suffix 
      { \statements } " 
    -> 
    " \constructor_name(\arguments): 
      \initializer_list_prefix, 
      \member2_name(\initializer2), 
      \member1_name(\initializer1), 
      \initialize_list_suffix 
      { \statements } " 
     if is_wrong_order(constructor_name,member1_name,member2_name); 

Cette règle trie essentiellement les initialiseurs. [Notez qu'il s'agit d'une sorte de bulle: mais les listes d'initialisation ont tendance à ne pas être longues, et vous ne l'exécuterez qu'une fois par constructeur.] Vous devriez exécuter cette règle après avoir levé tous les initialiseurs du corps du constructeur en utilisant les règles montrées plus tôt.