Конструктор рефакторинга для использования списка инициализации

Я работаю над очень большой базой кода (более 3 млн. мест), у нас, очевидно, много классов, но большинство из них не используют списки инициализации в своих конструкторах, вместо этого они присваивают значения в теле конструктора (некоторая часть кода была написана давным-давно, так что это стало стандартом де-факто). Возможно, они оптимизированы компилятором, но я не уверен, что это действительно так.

Я пытаюсь продвигать использование списков инициализации, но существует большая база кода, которую необходимо обновить, поэтому есть ли какие-либо инструменты, которые сделают это для меня автоматически? Наведите его на класс, найдите все строки m_var = 0; и переместите их в список инициализации (создав его при необходимости).

Помимо преобразования инициализации в теле в списки инициализации, есть ли способ узнать, что переменные-члены инициализируются в правильном порядке (т.е. в том же порядке, в котором они определены в заголовочном файле класса? Я надеялся, что CppCheck взял бы это, но это не кажется.


person fwg    schedule 14.11.2016    source источник
comment
Что касается второй части: GCC имеет -Wreorder.   -  person Biffen    schedule 14.11.2016
comment
@Biffen делает Visual Studio 2012? Так как это то, что я использую.   -  person fwg    schedule 14.11.2016
comment
Не то, чтобы я знал об этом, и быстрый гугл ничего не дал. Я оставлю это вам, чтобы сделать больше исследований по этому вопросу.   -  person Biffen    schedule 14.11.2016


Ответы (2)


Здравствуйте, я разработчик cppcheck.

Cppcheck также имеет проверку на несоответствие порядка. Но это безрезультатная проверка.

Например:

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

Вывод Cppcheck:

daniel@debian:~/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.

Наша простая проверка просто предупредит о несоответствии заказа. Вот почему это неубедительно. В приведенном выше коде порядок инициализации на самом деле не имеет значения, поскольку все члены в приведенном выше коде являются целыми числами, а все инициализаторы являются постоянными литералами.

person Daniel Marjamäki    schedule 17.11.2016

С масштабом, подобным размеру кодовой базы OP, необходима система преобразования программ (PTS). Это инструмент, который анализирует исходный файл на целевом языке в структуры данных компилятора (обычно AST), позволяет вам применять преобразования к AST, а затем может регенерировать действительный исходный код, включая исходные комментарии для измененной программы. Думайте о PST как об инструментах для рефакторинга в целом.

Хороший PTS позволит вам написать преобразования источника в источник в форме:

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

где это и это выражены в синтаксисе целевого языка, где это соответствует, только если исходный код соответствует явному синтаксису. [Это не совпадения строк; они работают с AST, поэтому компоновка не влияет на их способность сопоставляться].

Вам нужно ключевое правило, которое выглядит примерно так:

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 } ";

Здесь объясняется синтаксис этих правил/шаблонов для нашего набора инструментов для реинжиниринга программного обеспечения DMS. . DMS — это единственная известная мне PTS с преобразованием исходного кода, которая может работать с C++; он даже поддерживает диалект MSVS].

Я пропустил возможно необходимую проверку "if условие", что имя члена действительно является членом класса, при условии, что ваши конструкторы не являются оскорбительными.

Поскольку ваши конструкторы могут не иметь никакого списка инициализаторов, вам нужно вспомогательное правило, чтобы ввести его, когда это необходимо:

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 ; } "

Вам обязательно понадобятся дополнительные правила для других особых случаев, но их должно быть не более нескольких.

Что касается проверки порядка инициализации, вы можете запустить такую ​​проверку, используя шаблон (DMS):

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);

который требует вспомогательного мета-предиката, проверяющего порядок и жалующегося на неправильный порядок. Вам нужно имя_конструктора, чтобы предикат мог искать соответствующий класс и проверять порядок членов. [DMS предоставляет средства для доступа к таблице символов с помощью этого Информация].

В качестве альтернативы вы можете просто изменить их порядок, используя другое правило перезаписи:

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);

Это правило по существу сортирует инициализаторы. [Обратите внимание, что это пузырьковая сортировка: но списки инициализаторов, как правило, не длинные, и вы в любом случае будете запускать это только один раз для каждого конструктора.] Вы должны запустить это правило после того, как вы подняли все инициализаторы из тела конструктора, используя правила, показанные ранее.

person Ira Baxter    schedule 20.11.2016