Pergunta

Como você iria sobre como converter um razoavelmente grande (> 300 K), bastante maduro C codebase para C ++?

O tipo de CI tem em mente é dividida em arquivos que corresponde aproximadamente módulos (ou seja, menos granular do que um OO típico decomposição baseada em classe), utilizando ligação interna em vez funções privadas e de dados e ligação externa para funções públicas e dados . As variáveis ??globais são usados ??extensivamente para a comunicação entre os módulos. Há um muito extenso conjunto de testes de integração disponíveis, mas nenhuma unidade (ou seja módulo) testes de nível.

Eu tenho em mente uma estratégia geral:

  1. compilar tudo em C ++ 's subconjunto C e obter esse trabalho.
  2. módulos converter em aulas enormes, de modo que todas as referências cruzadas são escopo por um nome da classe, mas deixando todas as funções e dados como membros estáticos, e obter esse trabalho.
  3. Converter aulas enormes em instâncias com construtores apropriados e referências cruzadas inicializados; substituir acessos membro estático com acessos indiretos, conforme o caso; e obter esse trabalho.
  4. Agora, a abordagem do projeto como uma aplicação OO mal consignado, e testes de unidade de gravação, onde as dependências são tratáveis, e se decompõem em classes separadas onde eles não são; O objetivo aqui seria para mover de um programa de trabalho para outro em cada transformação.

Obviamente, isso seria um pouco de trabalho. Existem estudos de casos / histórias de guerra lá fora, sobre este tipo de tradução? estratégias alternativas? Outro conselho útil?

Nota 1:. O programa é um compilador, e, provavelmente, milhões de outros programas dependem de seu comportamento não mudar, reescrita tão atacado é muito bonito não é uma opção

Nota 2: a fonte é cerca de 20 anos, e tem talvez 30% rotatividade do código (linhas modificadas + adicionados / linhas totais anteriores) por ano. É fortemente mantido e ampliado, em outras palavras. Assim, um dos objetivos seria aumentar mantainability.

[Por causa da questão, suponha que a tradução em C ++ é obrigatório, e que deixá-lo em C é não uma opção. O ponto de adicionar esta condição é eliminar os "deixá-lo em C" respostas.]

Foi útil?

Solução

Depois de ter acabado de começar em praticamente a mesma coisa há alguns meses (em um projeto comercial de dez anos de idade, originalmente escrito com o "C ++ não é senão C com structs inteligentes" filosofia), gostaria de sugerir usando o mesmo estratégia você usaria para comer um elefante: levar uma mordida de cada vez. : -)

Tanto quanto possível, dividi-la em etapas que podem ser feitas com efeitos mínimos sobre outras peças. Construir um sistema de fachada, como Federico Ramponi sugerido, é um bom começo - uma vez que tudo tem um C ++ fachada e está se comunicando através dele, você pode alterar os internos dos módulos com justa certeza de que eles não podem afetar qualquer coisa fora deles.

Nós já tínhamos um sistema parcial C ++ interface no lugar (devido a anteriores esforços de refatoração menores), pelo que esta abordagem não foi difícil no nosso caso. Uma vez que tínhamos tudo comunicando como objetos C ++ (que levou algumas semanas, trabalhando em um ramo de código-fonte completamente separada e integrando todas as alterações para o ramo principal como foram aprovados), era muito raro que não poderíamos compilar uma totalmente versão de trabalho antes de sairmos para o dia.

A transição não está completo ainda - que já parou duas vezes para versões intermédias (buscamos um ponto de liberar a cada poucas semanas), mas é no bom caminho, e nenhum cliente se queixou sobre quaisquer problemas . Nosso QA pessoas só ter encontrado um problema que me lembro, também. : -)

Outras dicas

E:

  1. Compilar tudo em C subconjunto do C ++ e obter esse trabalho, e
  2. A implementação de um conjunto de fachadas deixando o código C inalterado?

Por que é "a tradução em C ++ obrigatória"? Você pode quebrar o código C sem a dor de convertê-lo em classes enormes e assim por diante.

A sua aplicação tem muita gente trabalhando nisso, e uma necessidade de não-ser quebrado. Se você é sério sobre a conversão em larga escala para um estilo OO, o que que você precisa é ferramentas de transformação maciças para automatizar o trabalho.

A idéia básica é para designar grupos de dados como classes, e depois obter a ferramenta para refatorar o código para mover os dados em classes, mover funções em apenas que os dados para essas classes, e rever todos os acessos a esses dados para insta as classes.

Você pode fazer uma pré-análise automatizada para formar grupos de estatística para obter algumas idéias, mas você ainda vai precisar de um engenheiro ciente applicaiton para decidir o que elementos de dados devem ser agrupados.

Uma ferramenta que é capaz de fazer esta tarefa é a nossa DMS Software Reengineering Toolkit . DMS tem analisadores fortes C para a leitura de seu código, capta o código C como árvores de compilador abstrato sintaxe, (e ao contrário de um compilador convencional) pode calcular análises de fluxo em toda a sua 300K SLOC. DMS tem uma extremidade C ++ frente que pode ser usado como o fim "de volta"; se escreve transformações que mapa C sintaxe para sintaxe C ++.

Uma das principais C ++ reengenharia tarefa em um grande sistema de aviônicos dá alguma idéia do que utilizando DMS para este tipo de atividade é semelhante. Ver artigos técnicos em www.semdesigns.com/Products/DMS/DMSToolkit.html, especificamente Re-engenharia modelos de componentes C ++ Transformação Programa automático Via

Este processo não é para os fracos de coração. Mas do que ninguém que iria considerar refatoração manual de um aplicativo grande já não tem medo de trabalho duro.

Sim, eu sou associado com a empresa, sendo o seu principal arquiteto.

Eu ia escrever classes C ++ sobre a interface C. Não tocar no código C vai diminuir a chance de estragar e acelerar o processo de forma significativa.

Depois de ter seu C ++ interagir-se; então é uma tarefa trivial de cópia + colar o código em suas classes. Como você mencionou -. Durante essa etapa, é vital para fazer testes de unidade

GCC está atualmente em midtransition para C ++ a partir de C. Eles começaram movendo tudo para o subconjunto comum de C e C ++, obviamente. Ao fazerem isso, eles acrescentaram avisos para GCC para tudo que encontraram, encontrado em -Wc++-compat. Que deverá fazê-lo na primeira parte de sua viagem.

Para as últimas partes, uma vez que você realmente tem tudo compilar com um compilador C ++, gostaria de focar substituindo coisas que têm homólogos idiomáticas C ++. Por exemplo, se você estiver usando listas, mapas, jogos, bitvectors, hashtables, etc, que são definidos usando macros C, você provavelmente vai ganhar um monte movendo-os para C ++. Da mesma forma com OO, você provavelmente encontrará benefícios que você já está usando uma linguagem C OO (como inheritence struct), e onde C ++ irá permitir uma maior clareza e melhor verificação de tipo em seu código.

A sua lista parece bem, exceto eu sugiro rever o conjunto de teste primeiro e tentar obter que tão apertada quanto possível antes de fazer qualquer codificação.

Vamos jogar outra idéia estúpida:

  1. compilar tudo em C ++ 's subconjunto C e obter esse trabalho.
  2. Comece com um módulo, convertê-lo em uma enorme classe, em seguida, em um exemplo, e construir uma interface C (idêntico ao que você começou a partir) daquela instância. Deixe o restante trabalho de código C com a interface C.
  3. Refactor, conforme necessário, o crescimento da OO subsistema fora do código C um módulo de cada vez, e soltar partes da interface C quando eles se tornam inúteis.

Provavelmente, duas coisas a considerar além de como você deseja iniciar estão no que você quer focagem , e onde você quer parada .

estado-lhe que há uma grande rotatividade do código, isso pode ser uma chave para foco seus esforços. Eu sugiro que você escolher as partes do seu código onde é necessária muita manutenção, o vencimento / partes estáveis ??são aparentemente funcionando bem o suficiente, por isso é melhor deixá-los como eles são, com exceção, provavelmente por alguma vitrine com fachadas etc.

Onde você quer parar depende de qual é a razão para querer converter para C ++. Isso dificilmente pode ser um objectivo em si. Se é devido a alguma 3ª dependência partido, concentrar seus esforços na interface para esse componente.

O software eu trabalho em uma enorme, base de código antigo, que foi 'convertido' de C para C ++ anos atrás agora. Acho que foi porque o GUI foi convertido para o Qt. Mesmo agora, ainda na maior parte parece com um programa C com classes. Quebrar as dependências causadas por membros de dados públicos, e refatoração as enormes classes com métodos monstro processuais em métodos menores e classes nunca realmente decolou, eu acho, pelos seguintes motivos:

  1. Não há necessidade de alterar o código que está funcionando eo que não precisa de ser reforçada. Se o fizer, introduz novos bugs sem adicionar funcionalidade, e os usuários finais não apreciam isso;
  2. É muito, muito difícil de fazer refactor confiável. Muitos pedaços de código são tão grandes e também tão vital que as pessoas quase não ouso tocá-lo. Temos um bastante extenso conjunto de testes funcionais, mas as informações de cobertura de código suficiente é difícil de obter. Como resultado, é difícil estabelecer se já existem provas suficientes no local para detectar problemas durante a refatoração;
  3. O ROI é difícil de estabelecer. O usuário final não vai beneficiar de refatoração, por isso deve estar em custo de manutenção reduzido, o que vai aumentar inicialmente devido pela refatoração você introduzir novos bugs em amadurecer, ou seja, código razoavelmente livre de bugs. E a própria refatoração será caro também ...

NB. Acho que você sabe que o "trabalho de forma eficaz com código Legacy" livro?

Você menciona que a ferramenta é um compilador, e que: "Na verdade, a correspondência de padrão, não basta digitar correspondência, no despacho múltiplo seria ainda melhor"

.

Você pode querer dar uma olhada maketea . Ele fornece a correspondência de padrão para ASTs, bem como a definição AST de uma gramática abstrato, e os visitantes, Tranformers, etc.

Se você tem um projeto pequeno ou acadêmica (digamos, menos de 10.000 linhas), uma reescrita é provavelmente a sua melhor opção. Você pode incluí-lo como quiser, e não vai demorar muito tempo.

Se você tem uma aplicação no mundo real, eu sugiro começá-lo para compilar como C ++ (que normalmente significa fixação principalmente se protótipos de função e assim por diante), então o trabalho em refatoração e empacotamento OO. Claro, eu não concordo com a filosofia de que as necessidades de código para ser OO estruturada, a fim de ser um código aceitável C ++. Eu faria uma conversão peça por peça, reescrita e refatoração como você precisa (para a funcionalidade ou para incorporar o teste de unidade).

Aqui está o que eu faria:

  • Uma vez que o código é de 20 anos de idade, sucata para baixo o analisador analisador / sintaxe e substituí-lo com um dos mais novos lex / yacc / bison (ou algo similar) etc baseada em código C ++, muito mais sustentável e mais fácil de entender. Mais rápido para desenvolver também, se você tem um BNF útil.
  • Uma vez que este está adaptado para o código antigo, comece envolvendo módulos em classes. Substitua as variáveis ??globais / compartilhados com interfaces.
  • Agora que você tem será um compilador em C ++ (não muito embora).
  • Desenhe um diagrama de classe de todas as classes no seu sistema, e ver como eles estão se comunicando.
  • Desenhe outro usando as mesmas classes e ver como eles devem se comunicar.
  • refatorar o código para transformar o primeiro diagrama para o segundo. (Isso pode ser confuso e complicado)
  • Lembre-se de usar o código C ++ para todos novo código adicionado.
  • Se você tiver algum tempo livre, tente substituir estruturas de dados, um por um para usar a STL ou impulso mais padronizado.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top