Есть ли инструмент для добавления идентификатора переопределения в существующий код C ++ - PullRequest
19 голосов
/ 03 сентября 2011

Задание

Я пытаюсь выяснить, как лучше всего добавить идентификатор C ++ 0x override ко всем существующим методам, которые уже переопределяются в большом объеме кода C ++, не делая это вручную.

(У нас есть много, много сотен тысяч строк кода, и выполнение этого вручную было бы совершенно непростым делом.)

Текущая идея

Наши стандарты кодирования говорят, что мы должны добавить ключевое слово virtual против всех неявно виртуальных методов в производных классах, даже если они абсолютно не нужны (для облегчения понимания).

Поэтому, если бы я сам написал сценарий добавления, я написал бы сценарий, который считывал бы все наши заголовки, находил все функции, начиная с виртуального, и вставлял override перед следующей точкой с запятой. Затем скомпилируйте его на компиляторе, который поддерживает override, и исправьте все ошибки в базовых классах.

Но я бы предпочел не использовать этот доморощенный способ, как:

  • очевидно, что это будет утомительно и подвержено ошибкам.
  • не все помнят, чтобы каждый раз добавлять виртуальное ключевое слово, поэтому этот метод пропустил бы некоторые существующие переопределения

Существует ли существующий инструмент?

Итак, есть ли уже инструмент, который анализирует код C ++, обнаруживает существующие методы, которые переопределяют, и добавляет override к их объявлениям?

(Мне известны такие инструменты статического анализа, как PC-lint , которые предупреждают о функциях, которые выглядят так, как будто они должны быть переопределены. То, что мне нужно, - это то, что на самом деле испортит наш код, так что будущие ошибки в переопределениях будут обнаруживаться во время компиляции, а не позже при статическом анализе)

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

Заранее спасибо ...

Ответы [ 4 ]

14 голосов
/ 12 апреля 2013

В рамках проекта LLVM разрабатывается инструмент под названием "cpp11-migrate", который в настоящее время имеет следующие функции:

  • преобразование циклов в циклы на основе диапазона для
  • convertконстанты нулевого указателя (например, NULL или 0) для C ++ 11 nullptr
  • замените спецификатор типа в объявлениях переменных на auto спецификатор типа
  • добавьте override спецификатор применимых функций-членов

Этот инструмент задокументирован здесь и должен быть выпущен как часть clang 3.3.Однако вы можете загрузить исходный код и создать его самостоятельно сегодня.

Редактировать

Дополнительная информация:

Редактировать2: 2013-09-07

"cpp11-migrate" переименован в " clang-modernize ".Для пользователей Windows теперь он включен в новые LLVM Snapshot Builds .

7 голосов
/ 03 сентября 2011

Наш инструментарий реинжиниринга программного обеспечения DMS с поддержкой C ++ 11 C ++ Front End может сделать это.

DMS - система преобразования программ общего назначения для произвольных языков программирования; внешний интерфейс C ++ позволяет ему обрабатывать C ++. DMS анализирует, строит AST и таблицы символов, которые являются точными (это трудно сделать для C ++), обеспечивает поддержку запроса свойств узлов и деревьев AST, допускает процедурные и исходные преобразования в дереве. После внесения всех изменений модифицированное дерево можно восстановить с сохранением комментариев.

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

    source domain Cpp.  -- tells DMS the following rules are for C++

    rule insert_virtual_keyword (n:identifier, a: arguments, s: statements):
       method_declaration -> method_declaration " =
       " void \n(\a) { \s } "  ->  " virtual void \n(\a) { \s }"
       if is_implicitly_virtual(n).

Такие правила соответствуют синтаксическим деревьям, поэтому они не могут не совпадать с комментарием, строкой или чем-либо еще. Смешные кавычки не являются строковыми кавычками C ++; они являются мета-кавычками, позволяющими языку правил знать, что то, что находится внутри них, должно рассматриваться как синтаксис целевого языка («Cpp»). Обратная косая черта - это экранирование от текста на целевом языке, что позволяет сопоставлять произвольные структуры, например, \ a указывает на необходимость «a», которое определено как синтаксическая категория «аргументы».

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

Интересная часть - реализация предиката (возвращающего TRUE или FALSE), управляющего применением преобразования: is_implicitly_virtual. Этот предикат принимает (абстрактное синтаксическое дерево) имя метода n. Этот предикат будет обращаться к полной таблице символов C ++, чтобы определить, что в действительности представляет собой n. Мы уже знаем, что это метод только из его синтаксической установки, но мы хотим знать, в каком контексте класса. Таблица символов обеспечивает связь между методом и классом, а информация таблицы символов для класса говорит нам, от чего наследуется класс, и для тех классов, какие методы они содержат и как они объявлены, что в конечном итоге приводит к открытию (или не) что метод родительского класса является виртуальным. Код для этого должен быть реализован как процедурный код, идущий против API таблицы символов C ++. Однако вся тяжелая работа выполнена; таблица символов верна и содержит ссылки на все другие необходимые данные. (Если у вас нет этой информации, вы не можете принять алгоритмическое решение, и любые изменения кода, скорее всего, будут ошибочными).

В прошлом DMS использовался для внесения значительных изменений в код C ++ с использованием программных преобразований (см. Раздел «Статьи» на веб-сайте по темам реархитектуры C ++).

(Я не эксперт по C ++, просто архитектор DMS, поэтому, если я ошибаюсь, пожалуйста, прости.)

2 голосов
/ 03 сентября 2011

Я сделал что-то подобное несколько месяцев назад с объемом кода около 3 МБ, и, хотя вы говорите, что «делать это вручную было бы непростым началом», я думаю, что это единственный способ. Причина в том, что вы должны применять ключевое слово override к прототипам, которые предназначены для переопределения методов базового класса. Любой инструмент, который добавляет его, помещает его в прототипы, которые фактически переопределяют методы базового класса. Компилятор уже знает, какие это методы, поэтому добавление ключевого слова ничего не меняет. (Обратите внимание, что я не очень знаком с новым стандартом, и я предполагаю, что ключевое слово override является необязательным. Visual Studio поддерживает переопределение, по крайней мере, с VS2005.)

Я использовал поиск «виртуальный» в заголовочных файлах, чтобы найти большинство из них, и до сих пор иногда нахожу другой прототип, в котором отсутствует ключевое слово override.

Я нашел две ошибки, пройдя через это.

0 голосов
/ 04 сентября 2011

Eclipse CDT имеет работающий синтаксический анализатор C ++ и семантические утилиты.В последней версии IIRC также есть маркеры для переопределения методов.

Не потребовалось бы большого кода для написания плагина, который бы основывался на этом, и переписал бы код, чтобы он содержал теги override, где это необходимо.

...