Tenho uma sólida experiência em Java / Groovy e fui designado para uma equipe que mantém uma grande base de código C para um software administrativo.
Alguns pontos problemáticos, como lidar com blob no banco de dados ou gerar relatórios em PDF e Excel, foram externalizados para o serviço web java.
No entanto, como desenvolvedor Java, estou um pouco confuso com alguns aspectos do código:
- é detalhado (especialmente quando se lida com 'exceção')
- existem muitos métodos enormes (muitos mais de 2000 linhas)
- não há estruturas de dados avançadas (sinto muita falta de Listar, Definir e Mapear)
- sem separação de preocupações (o SQL é alegremente misturado em todo o código)
Como resultado, sinto que o negócio está oculto em toneladas de código técnico e meu cérebro, moldado com Orientação a Objetos e uma pitada de programação Funcional, não fica à vontade.
O lado bom do projeto é que o código é direto: não há estrutura, nem manipulação de código de bytes em tempo de execução, nem AOP. E o servidor pode atender simultaneamente a mais de 10000 usuários com uma única máquina usando menos memória do que o java precisa cuspir "olá mundo".
Quero aprender a escrever código C de acordo com os princípios modernos comumente aceitos. Existe algum princípio comumente aceito sobre como o C moderno deve ser escrito e estruturado?
Algo parecido com o equivalente ao livro 'Effective Java', mas para C.
Edite à luz das respostas e comentários:
- Vou tentar adaptar minha mentalidade ao código C e não tentar espelhá-lo no OOP.
- Comecei a ler os guias de estilo de codificação recomendados a partir do comentário (Os padrões de codificação GNU e O estilo de codificação do kernel do Linux).
- Tentarei propor esse estilo de código aos meus colegas de trabalho. A parte mais difícil pode ser convencer os colegas de trabalho de que um método enorme pode ser dividido em partes menores e que repetir as mesmas 4 linhas de código de tratamento de erros pode ser evitado com a ajuda de um método.
fonte
Respostas:
Posso ler da sua pergunta que o problema não é que o código seja C antigo, mas apenas uma programação ruim . A maioria dos problemas que você mencionou, como verbosidade, imensas funções de mais de 2000 linhas ou nenhuma separação de interesses, é aplicável a qualquer idioma, C ou Java.
A verbosidade foi mencionada no contexto do tratamento de erros. Você não forneceu um exemplo, portanto, só posso lembrar que o código de tratamento de erros também é código . Não há desculpa para seções repetitivas do código padrão. Fatore isso; para uma função ou (se não valer a pena criar uma função separada), faça o
goto Error;
padrão e mova a manipulação de erros e a limpeza de recursos para umaError:
seção na parte inferior da função.Se passar o erro pela cadeia de chamadas parece ser o problema, pergunte-se: a função lá em cima realmente precisa saber que algum rapaz aqui embaixo teve algum problema? Os mecanismos de exceção embutidos em uma linguagem facilitam a tarefa, mas, em geral, é melhor lidar com exceções mais cedo (em qualquer idioma) para que a condição de erro não polua a lógica do código de alto nível. E se a função lá em cima realmente precisa saber, há maneiras de emular exceções com
setjmp
elongjmp
.Eu acho que o único problema realmente relacionado ao C mencionado é a falta de contêineres padrão. Embora
Set
possa, em geral, ser substituído por uma matriz classificada eMap
(na maior parte) por uma matriz de pares ou astruct
(se você souber a chave definida anteriormente, semap[key] = value
transforma ems.key = value
), mas o fato é que não há contêiner dinâmico de matriz no padrão biblioteca. No C99, você pode pelo menos declarar uma matriz de comprimento variável na pilha (int array[len]
), mas precisa calcularlen
antecipadamente (geralmente não é difícil) e, é claro, não pode devolvê-la como qualquer objeto alocado pela pilha. A maioria dos projetos acaba criando seu próprio contêiner de matriz dinâmica ou adotando um contêiner de código aberto.Em uma nota final, gostaria de salientar que já estive lá. Fui o programador Java que se mudou para C ++ e C. puro. Gostaria de aconselhar "leia o livro X para aprender um bom C", mas não existe um como o Java. O caminho a seguir é absorver todas as complexidades da linguagem e da biblioteca padrão; pesquise bastante no Google, leia muito e codifique bastante até começar a pensar em C. Tentar escrever coisas em C como faria em Java é tão frustrante quanto tentar escrever uma frase em um idioma estrangeiro com palavras traduzidas diretamente de sua mãe língua; você e o leitor se encolherão. A boa notícia é que aprender boa programação é lento, mas aprender outro idioma é rápido. então, se você escrever um código decente em Java,
fonte
setjmp()
/longjmp()
como uma ferramenta válida: ela nem tenta executar qualquer limpeza. Qualquer alocação será vazada, qualquer bloqueio retido não será liberado, qualquer arquivo aberto não será fechado e qualquer inconsistência temporária nos dados se tornará permanente. IMHO, esse par de funções é basicamente o pior hack já inventado, com a única justificativa de que foi possível implementá-lo. No final, existe realmente apenas uma maneira válida de lidar com erros em C: códigos de erro explícitos.setjmp/longjmp
parece um peixe fora d'água em C e eu nunca o usei. Senti-me compelido a incluí-los apenas por causa dos inúmeros tutoriais / bibliotecas da Internet para imitar exceções, então pensei que existem pessoas que realmente o usam.Eu recomendo que você seja cauteloso se isso vale o seu tempo e o dinheiro da empresa para gastar recursos na "modernização" de um software funcional com baixa complexidade de código e com bom desempenho. É provável que você introduza novos bugs, especialmente porque parece ser um sistema com o qual você não está familiarizado.
Se você ainda deseja seguir esse caminho, sugiro o seguinte:
Nesse ponto, você decidirá se vale a pena explorar isso. Se a cultura da sua empresa não recompensar o fracasso, obtenha a luz verde de um gerente superior ou superior.
Eu acho que esse é um bom roteiro e o levamos aonde você precisa. Sem conhecer as especificidades deste projeto, é difícil ajudá-lo. Por favor, não descarte meu aviso como excessivamente alarmista. Toneladas de excelentes programadores derrotaram o pó, tentando reescrever um projeto existente no seu idioma favorito ou usando ferramentas "modernas". Essa é uma decisão que deve ser cuidadosamente pensada, e peço que você não seja desonesto e faça isso sozinho, sem o apoio da gerência ou a assistência de seus colegas.
fonte
Se você preferir uma linguagem de nível superior, existem algumas linguagens como C ++ ou Objective-C que podem ser misturadas com o código C com bastante facilidade.
Como alternativa, C e C ++ são razoavelmente compatíveis. Você pode apenas compilar toda a base de código como C ++ com poucas alterações - terá a variável ocasional denominada "classe" ou "modelo" que precisa renomear, mas na prática isso será tudo. (sizeof ('a') é diferente em C e C ++, mas acho que nunca usei isso).
Se você seguir esse caminho, considere que o próximo mantenedor pode não ser muito fluente com o C ++. Não se empolgue. Aproveite o C ++, mas apenas até o momento em que um programador de C possa entender com facilidade.
fonte
malloc
) é considerado uma prática ruim em C. O significadoconst
einline
também é muito diferente entre C e C ++ e, é claro, C ++ não entende__restrict
. Não trate os idiomas como intercambiáveis, mesmo no subconjunto de fontes que compilam em ambos.Basicamente, escrever um bom código C é o mesmo que escrever um bom código C ++ ou Java: você deseja uma classe, use a
struct
. Você deseja herança, inclua a basestruct
como um primeiro membro sem nome. Você deseja funções virtuais, adicione um ponteiro a uma estáticastruct
de ponteiros de função. E assim por diante, etc. É exatamente o que o C ++ faz sob o capô, a única diferença é que é explícito em C. Assim, você pode fazer uma programação perfeitamente orientada a objetos em C, apenas um pouco diferente e mais compacta do que você estão acostumados.O ponto é que a boa programação é sobre paradigmas, não sobre recursos da linguagem. É verdade que é sempre bom que os recursos do idioma ofereçam um bom suporte aos paradigmas que você deseja usar, mas os recursos do idioma não são um requisito. Depois que você perceber isso, poderá escrever um bom código em praticamente qualquer idioma (além de algumas línguas esotéricas, como brainfuck ou INTERCAL, ou seja).
Obviamente, o problema continua sendo que a biblioteca C padrão não contém nenhuma dessas classes de contêiner bacanas com as quais você está acostumado. Infelizmente, isso significa que você precisará usar o seu próprio produto ou solucionar essa falta usando matrizes alocadas dinamicamente. Mas eu aposto que você descobrirá em breve que tudo o que você realmente precisa são matrizes dinâmicas (
malloc()
) e listas / árvores vinculadas que são implementadas por meio de membros de ponteiro em suas classes.fonte