Вопрос по c++, program-transformation, refactoring, c, legacy – Преобразование источника C в C ++

41

Как бы вы пошли на преобразование достаточно большой (>300K), достаточно зрелая кодовая база C для C ++?

Тип C, который я имею в виду, разбивается на файлы, примерно соответствующие модулям (то есть менее детализированные, чем типичная декомпозиция на основе классов ОО), используя внутреннюю связь вместо частных функций и данных и внешнюю связь для открытых функций и данных. Глобальные переменные широко используются для связи между модулями. Существует очень обширный набор тестов интеграции, но нет тестов уровня юнитов (то есть модулей).

Я имею в виду общую стратегию:

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

Очевидно, это будет довольно много работы. Существуют ли какие-либо тематические исследования / военные истории о таком переводе? Альтернативные стратегии? Другие полезные советы?

Примечание 1: программа является компилятором, и, вероятно, миллионы других программ полагаются на то, что ее поведение не меняется, поэтому массовое переписывание практически невозможно.

Примечание 2: источнику почти 20 лет, и, возможно, 30% оттока кода (измененные строки + добавленные / предыдущие общие строки) в год. Другими словами, он сильно поддерживается и расширяется. Таким образом, одной из целей будет повышение управляемости.

[Ради вопроса, предположим, что перевод наC ++ является обязательным, и что оставить его в Cне опция. Смысл добавления этого условия состоит в том, чтобы отсеять "оставь это в С " ответы.]

Перевод не обязателен, он только ради вопроса (чтобы отсеять тех "дон»переводить " ответы). Сроки могут быть 1-10 лет (этоэто долгожданная программа). Barry Kelly
Какие временные рамки для обязательной миграции? paxos1977
Насколько хорошо вы знаете базу кода C? Внутри & Из? paxos1977
Просто из любопытства, почему вы хотите перевести свой код с C на C ++? Какие'не так просто оставить его в C? Chris Lutz
О, я вижу, чтотвой компилятор! Federico A. Ramponi

Ваш Ответ

11   ответов
5

Я написал бы классы C ++ через интерфейс C. Не прикасайтесь к коду C уменьшит вероятность путаницы и значительно ускорит процесс.

Как только у вас есть интерфейс C ++; тогда это тривиальная задача копирования + вставки кода в ваши классы. Как вы упомянули - на этом этапе очень важно выполнить модульное тестирование.

"С интерфейс " начинается и заканчивается "главный()", Я думаю, что вы, возможно, оставили несколько шагов ... :) Barry Kelly
2

Вы упоминаете, что ваш инструмент является компилятором, и что: "На самом деле, сопоставление с образцом, а не просто сопоставление типов, при многократной отправке было бы еще лучше ».

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

3

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

Набор тестов довольно плотный, поверь мне. 20 лет QA с десятками тысяч зарегистрированных ошибок с написанными тестовыми примерами имеют тенденцию делать это. Barry Kelly
3

Позволять'Еще одна глупая идея:

Скомпилируйте все в C ++ 'S подмножество и получить, что работает.Начните с модуля, преобразуйте его в огромный класс, затем в экземпляр и создайте интерфейс C (идентичный тому, с которого вы начали) из этого экземпляра. Пусть оставшийся C-код работает с этим C-интерфейсом.Выполните рефакторинг по мере необходимости, увеличивая подсистему ОО из кода C по одному модулю за раз, и отбрасывайте части интерфейса C, когда они становятся бесполезными.
Да, это примерно части 1 & 2 моего плана, более подробно. Barry Kelly
11

Как насчет:

Компилирование всего в C ++ 'S подмножество и получить, что работает, иРеализация наборафасадов оставить код C без изменений?

Почему "перевод на С ++ обязательно? Вы можете обернуть C-код без необходимости конвертировать его в огромные классы и так далее.

"перевод на С ++ обязателен это отсеять те ответы, которые говорят "оставить C без изменений ". Barry Kelly
Кроме того, возможно, код C неЧистый Анси C, но это какой-то древний диалект C, который не былт чистый ANSI. :-) Warren P
Одной из задач повышения модульности кода, преобразования его в C ++ и добавления модульных тестов является повышение его удобства сопровождения. Просто положить фасад над фасадом просто выигралт делать Barry Kelly
7

Ваше приложение имеет много людей, работающих над ним, и не должны быть нарушены. Если вы серьезно относитесь к крупномасштабному преобразованию в ОО-стиль, вам нужны массивные инструменты преобразования для автоматизации работы.

Основная идея состоит в том, чтобы назначить группы данных как классы, а затем получить инструмент для рефакторинга кода, чтобы переместить эти данные в классы, переместить функции только с этими данными в эти классы и пересмотреть все обращения к этим данным для вызова классов ,

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

Инструмент, который способен выполнить эту задачу, является нашимDMS Software Reengineering Toolkit, DMS имеет сильные C-парсеры для чтения вашего кода, захватывает C-код как деревья абстрактного синтаксиса компилятора (и в отличие от обычного компилятора) может вычислять анализ потока по всему вашему SLOC 300K. DMS имеет интерфейс C ++, который можно использовать как "назад» конец; каждый пишет преобразования, которые отображают синтаксис C в синтаксис C ++.

Основная задача реинжиниринга C ++ в большой системе авионики дает некоторое представление о том, на что похоже использование DMS для этого вида деятельности. См. Технические документы по адресу www.semdesigns.com/Products/DMS/DMSToolkit.html, в частности о реинжиниринге моделей компонентов C ++ с помощью автоматического преобразования программы.

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

Да я'м связан с компанией, являясь ее главным архитектором.

хорошая публикация, но вы можете добавить, что вы связаны с указанным продуктом и компанией, в противном случае люди начнут указывать на это, называя вашу публикацию скрытой рекламой ;-) none
@ Нет - я знаю Ира из comp.compilers. Это преобразование, вероятно, не обсуждается нами не только из-за стоимости / риска, но и потому, что мыповторно исследовать другие пути. Тем не менее, ответ полезен для других людей с похожими проблемами ... Я ' Barry Kelly
@none: К сожалению, ТАК посетители нене понимаю ТАК согласованных соглашений. "Наши» является согласованным признаком принадлежности, давно отсортированным обсуждениями MetaStackoverflow. Ira Baxter
@ нет: если вы можете взломать свой путь через проблему через день или неделю, вы нене нужно DMS. Если вы думаете о человеко-месяцах или больше, чтобы выполнить работу, и / или вы должны быть уверены, что не сделалиЧтобы сломать код, вам нужен такой инструмент, как DMS. Ira Baxter
1

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

Если у вас есть реальное приложение, яЯ рекомендую заставить его скомпилировать как C ++ (что обычно подразумевает прежде всего исправление прототипов функций и т. п.), а затем работать над рефакторингом и переносом OO. Конечно, я неСогласитесь с философией, что код должен быть ОО-структурированным, чтобы быть приемлемым кодом C ++. Я'd выполнять поэтапное преобразование, переписывание и рефакторинг по мере необходимости (для функциональности или для включения модульного тестирования).

1

Вот'Что бы я сделал:

Поскольку коду уже 20 лет, оставьте анализатор синтаксического анализа / синтаксического анализатора и замените его одним из более новых кодов C ++ на основе lex / yacc / bison (или чем-нибудь подобным) и т. Д., Гораздо более понятным и понятным. Быстрее развиваться, если у вас есть под рукой BNF.Как только это будет установлено в старом коде, начните упаковывать модули в классы. Замените глобальные / общие переменные интерфейсами.Теперь то, что у вас будет, будет компилятором в C ++ (хотя и не совсем).Нарисуйте диаграмму классов всех классов в вашей системе и посмотрите, как они общаются.Нарисуйте еще один, используя те же классы, и посмотрите, как они должны общаться.Рефакторинг кода для преобразования первой диаграммы во вторую. (это может быть грязно и сложно)Не забудьте использовать код C ++ для всего нового добавленного кода.Если у вас есть время, попробуйте заменить структуры данных одну за другой, чтобы использовать более стандартизированные STL или Boost.
@Barry; Это звучит противно. Ape-inago
Обозреватель: язык не является LL (1) и LALR (1), он чувствителен к контексту способами, которые могут разрешать специальные семантические и синтаксические предикаты. Это цена гибкого языкового расширения на протяжении многих лет. Barry Kelly
Пересмотр 3: Это легко обосновать в рекурсивном спуске, написанном от руки, но совсем не легко проследить при переходах конечного автомата смещения / уменьшения. Так как этот токен курсора может появиться где угодно, это усложнит грамматику, как вы можете себе представить - это 'Это как явный WS, который нуждается в действии. Barry Kelly
Я неЯ думаю, вы цените все тонкости компиляторов. Коммерческие компиляторы используют рукописные лексеры и парсер по многим причинам, производительность которых одна. Во-вторых, нене слишком привязан к классам. Многократная диспетчеризация функций в стиле CLOS будет более полезной, чем виртуальные методы. Barry Kelly
4

GCC в настоящее время находится в середине перехода к C ++ из C. Они начали с того, что переместили все в общее подмножество C и C ++, очевидно. Сделав это, они добавили предупреждения в GCC за все, что они нашли в-Wc++-compat, Это должно помочь вам в первой части вашего путешествия.

Что касается последних частей, когда у вас все будет компилироваться с помощью компилятора C ++, я бы сосредоточился на замене вещей, которые имеют идиоматические аналоги C ++. Например, если выИспользуя списки, карты, наборы, битовые векторы, хеш-таблицы и т. д., которые определены с помощью макросов C, вы, вероятно, получите много, переместив их в C ++. Точно так же с ОО, выВероятно, вы найдете преимущества, если вы уже используете идиому C OO (например, структурное наследование) и где C ++ обеспечит большую ясность и лучшую проверку типов в вашем коде.

15

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

Насколько это возможно, разбейте его на этапы, которые можно сделать с минимальными эффектами на другие части. Построение фасадной системы, аФедерико Рампони это хорошее начало - как только все имеет фасад C ++ и обменивается данными через него, вы можете изменить внутренние компоненты с полной уверенностью, что они могут 'не влияет ни на что вне их.

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

Переключение непока не закончено - мыМы сделали паузу два раза для промежуточных релизов (мы нацелены на выпуск релизов каждые несколько недель), но это 'На подходе, и ни один клиент не жаловался на какие-либо проблемы. Наши специалисты по обеспечению качества обнаружили только одну проблему, которую я тоже помню. :-)

звучит страшно ... Вы должны написать более подробную статью об этой процедуре, держу пари, что она будет хорошо прочитана. Ape-inago
мы написали пару статей в блоге о конкретных частях конверсии,geekblog.oakcircle.com/2008/07/19/ascii-unicode-and-windows а такжеgeekblog.oakcircle.com/2009/03/15/superbug , Я'Я не достаточно забавный писатель, чтобы сделать все это интересным, хотя. Head Geek
3

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

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

Где вы хотите остановиться, зависит от причины, по которой вы хотите перейти на C ++. Это вряд ли может быть самоцелью. Если это связано с какой-либо сторонней зависимостью, сфокусируйте свои усилия на интерфейсе с этим компонентом.

Программное обеспечение, над которым я работаю, - это огромная, старая база кода, которая былапреобразованный» от C до C ++ лет назад. Я думаю, что это потому, что графический интерфейс был преобразован в Qt. Даже сейчас это все еще в основном похоже на C-программу с классами. Разбиение зависимостей, вызванных открытыми членами данных, и рефакторинг огромных классов с помощью процедурных методов-монстров на более мелкие методы и классы никогда по-настоящему не снимались, я думаю по следующим причинам:

Нет необходимости менять работающий код, который не нуждается в улучшении. Это приводит к появлению новых ошибок без добавления функциональности, и конечные пользователи нея ценю это;Надежность рефакторинга сделать очень и очень сложно. Многие фрагменты кода настолько велики и жизненно важны, что люди едва ли осмеливаются прикасаться к нему. У нас довольно обширный набор функциональных тестов, но получить достаточную информацию о покрытии кода сложно. В результате трудно установить, имеется ли уже достаточно тестов для выявления проблем во время рефакторинга;Окупаемость инвестиций сложно установить. Конечный пользователь не получит выгоду от рефакторинга, поэтому он должен быть в сниженных затратах на обслуживание, которые первоначально будут увеличиваться, потому что при рефакторинге вы вводите новые ошибки в зрелый, то есть довольно безошибочный код. И сам рефакторинг тоже будет дорогостоящим ...

NB. Я полагаю, вы знаете "Эффективная работа с кодом Legacy » книга?

Да, у меня есть книга. К сожалению, это почти полностью применимо только к тестируемому модулю коду. Главное предложение - чуть больше, чем абзац, который я помню, - для людей, использующих не-OO-код, было использование OO-варианта. Barry Kelly
Отток кода является значительным; Это'Не редкость, что некоторые подмножества функциональности переписаны достаточно полно. Например. генератор кода должен быть переписан для 64-битного, сканер адаптирован для Unicode, перегрузка отрегулирована для вывода общего метода и т. д. и т. д. Barry Kelly
Информация о большей части все еще похожа на C, с которой я могу жить. Как я уже сказал, существует значительный отток, так что возможность использовать C ++ для переписанных частей, движущихся вперед, все равно будет победой с точки зрения модульности. Barry Kelly
Нет'т действительностабильный» части, как таковые, кроме менеджера памяти. Основными целями было бы повышение уровня написанного исходного кода абстракции путем тщательного использования шаблонов, классов и уменьшения перекрестных зависимостей, особенно тех, которые вызваны глобальными переменными. Barry Kelly

Похожие вопросы