Рефакторинг C++ и боль в заднице

Mar 22, 2011 11:33

Когда N лет назад я искал и не нашёл рефакторинг C++ в Visual Studio, то был поражён в самое сердце.

Как же так, "Микрософт нас бросил!", "Как они могли?!", "Разве так поступают нормальные люди?!". При спокойном размышлении через пять минут я стал перебирать детали кошмарного синтаксиса этого языка и даже стал сочувствовать разработчикам VS. Нет, они не от хорошей жизни отказались от встроенного рефакторинга плюсового кода и отдали это поле разработчикам сторонних плагинов.

Приведу сегодняшний пример ручного рефакторинга, невозможного с точки зрения автоматических средств. Начать надо с того, что компилятор VS 2008 очень придирчив к исплользованию неконстантных методов на константных объектах и трактует такое как ошибку:

class foo{
public:
int bar();
...
};

const foo * f = ...;
f->bar(); //< Error C2662: cannot convert 'this' pointer from 'const foo' to 'foo &'

В проекте есть ветвистая иерархия классов с единым корнем, сложившаяся ещё во времена 386-х компьютеров и написанная скорее в процедурном стиле, чем в парадигме ООП. Естественно, речь о лучших практиках и паттернах проектирования не шла, модификатор const к соответствующим методам не применялся, но самое неприятное в другом: этот код критически важен и одновременно слишком наукоемок, чтобы его переписать с нуля в обозримое время. Как только в проекте появляется код, желающий принимать (указатели на) константные объекты, возникает ошибка C2662.

К.О.^WМартин Фаулер со товарищи спешит на помощь: здесь определенно нужен рефакторинг. Выход видится простой: прописать в корне иерархии нужные консты, а потом аналогично поправить всех потомков. Но оказывается, что потомков под сто штук, что методов надо поправить штук двадцать, методы в потомках объявлены по-разному (по 4 комбинации на каждый), и бездумно натравливать автозамену на исходники нельзя. Итого, 100*20*4 действий. Много! Ну ладно, можно просто 20*4, но это тоже немало. И усугубляет ситуацию то, что невозможно автоматически проверить, правильно ли всё сделано. В языке C++ нет возможности различить перегруженный метод с другой сигнатурой и унаследованный метод с ошибочно указанной сигнатурой. Так как у нас большинство вызовов делаются по указателю на базовый класс, то компилятор не может сделать предупреждения о неверном использовании метода или об отсутствующем методе. Достаточно пропустить одну замену - и в коде появится бомба замедленного действия.

Какие мне видятся решения. Одно неправильное, но быстрое: везде в новом коде не использовать константные объекты, дескать, доверять всем и везде. Другое правильное, но не избавленное от необходимости проверять результаты вручную: написать скрипт на перле для замены всех неправильных паттернов на правильные и натравить его на плюсовые файлы.

Правильного и автоматического решения я пока не знаю. Есть предложения?

refactoring, c++, programming, техническое

Previous post Next post
Up