Tengo una sólida experiencia en Java / Groovy y he sido asignado a un equipo que mantiene una base de código C bastante grande para un software administrativo.
Algunos puntos débiles, como tratar con blob en la base de datos o generar informes en PDF y Excel, se han externalizado al servicio web de Java.
Sin embargo, como desarrollador de Java, estoy un poco confundido por algunos aspectos del código:
- es detallado (especialmente cuando se trata de 'excepción')
- hay muchos métodos enormes (muchos métodos de más de 2000 líneas)
- no hay estructuras de datos avanzadas (extraño mucho List, Set and Map)
- sin separación de preocupaciones (SQL se mezcla alegremente en todo el código)
Como resultado, siento que el negocio está oculto en toneladas de código técnico y mi cerebro, conformado con objetos orientados y una pizca de programación funcional, no está a gusto.
El lado bueno del proyecto es que el código es sencillo: no hay marco, no hay manipulación de código de bytes en tiempo de ejecución, no hay AOP. Y el servidor puede responder simultáneamente a más de 10000 usuarios con una sola máquina utilizando menos memoria de la que Java necesita para escupir "hola mundo".
Quiero aprender a escribir código C de acuerdo con los principios modernos comúnmente aceptados. ¿Hay algún principio comúnmente aceptado sobre cómo se debe escribir y estructurar la C moderna?
Algo parecido al equivalente del libro 'Java efectivo', pero para C.
Edite a la luz de las respuestas y comentarios:
- Intentaré adaptar mi mentalidad al código C y no intentar reflejarlo en OOP.
- Comencé a leer y leer las guías de estilo de codificación recomendadas del comentario (The GNU Coding Standards y The Linux Kernel Coding Style).
- Luego trataré de proponer este estilo de código a mis compañeros de trabajo. La parte más difícil podría ser convencer a los compañeros de trabajo de que un método enorme podría dividirse en partes más pequeñas y que la ayuda de un método podría evitar las mismas 4 líneas de código de manejo de errores.
fuente
Respuestas:
Puedo leer de su pregunta que el problema no es que el código sea viejo C sino que simplemente sea una mala programación. La mayoría de los problemas que mencionó, como la verbosidad, las enormes funciones de más de 2000 líneas o la no separación de preocupaciones, se aplican a cualquier lenguaje, C o Java por igual.
Se mencionó la verbosidad en el contexto del manejo de errores. No proporcionó un ejemplo, así que solo puedo recordar que el código de manejo de errores también es código . No hay excusa para las secciones repetitivas del código repetitivo. Factorizarlo; ya sea a una función o (si no vale la pena crear una función separada) haga el
goto Error;
patrón y mueva el manejo de errores y la limpieza de recursos a unaError:
sección en la parte inferior de la función.Si pasar el error por la cadena de llamadas parece ser el problema, pregúntese: ¿la función allí arriba realmente necesita saber que algún pequeño aquí abajo tuvo un problema? Los mecanismos de excepción integrados en un lenguaje facilitan hacerlo, pero en general es mejor manejar las excepciones temprano (en cualquier idioma) para que la condición de error no contamine la lógica del código de alto nivel. Y si la función allí arriba realmente necesita saber, hay formas de emular excepciones con
setjmp
ylongjmp
.Creo que el único problema realmente relacionado con C mencionado es la falta de contenedores estándar. Si bien
Set
en general se puede reemplazar con una matriz ordenada yMap
(en su mayor parte) con una matriz de pares o unstruct
(si conoce el conjunto de claves de antemano, semap[key] = value
convierte ens.key = value
), pero el hecho es que no hay un contenedor de matriz dinámica en el estándar biblioteca. En C99, al menos puede declarar una matriz de longitud variable en la pila (int array[len]
), pero necesita calcular delen
antemano (por lo general, no es difícil) y, por supuesto, no puede devolverla como un objeto asignado a la pila. La mayoría de los proyectos terminan escribiendo su propio contenedor de matriz dinámica o adoptando uno de código abierto.En una nota final, me gustaría señalar que he estado allí. He sido el programador de Java que se mudó a C ++ y C. puro. Me gustaría aconsejar "leer el libro X para aprender un buen C", pero no hay ninguno como Java. El camino a seguir es absorber todas las particularidades del lenguaje y la biblioteca estándar; google mucho, lee mucho y codifica mucho hasta que comiences a pensar en C. Intentar escribir cosas en C como lo harías en Java es tan frustrante como intentar escribir una oración en un idioma extranjero con palabras directamente traducidas de tu madre lengua; tanto usted como el lector se avergonzarán. La buena noticia es que aprender una buena programación es lento, pero aprender otro idioma es rápido. así que si escribes código decente en Java,
fuente
setjmp()
/longjmp()
como una herramienta válida: ni siquiera intenta realizar ninguna limpieza. Cualquier asignación se filtrará, cualquier bloqueo retenido no se liberará, ningún archivo abierto se cerrará y cualquier inconsistencia de datos transitorios se volverá permanente. En mi humilde opinión, este par de funciones es básicamente el peor truco jamás inventado, con la única justificación de que fue posible implementarlo. Al final, en realidad solo hay una forma válida de manejar errores en C: códigos de error explícitos.setjmp/longjmp
parece un pez fuera del agua en C y nunca los usé. Me sentí obligado a incluirlos solo debido a los numerosos tutoriales / bibliotecas en Internet para imitar excepciones, por lo que pensé que hay personas que realmente lo usan.Le recomendaría que tenga cuidado de si vale la pena su tiempo y el dinero de la compañía para gastar recursos en "modernizar" un software que funcione con baja complejidad de código y que funcione bien. Es muy probable que introduzcas nuevos errores tú mismo, especialmente porque parece ser un sistema con el que no estás familiarizado.
Si todavía quieres seguir esa ruta, te sugiero lo siguiente:
En este punto, usted decidirá si vale la pena explorarlo o no. Si la cultura de su empresa no recompensa el fracaso, obtenga la luz verde de un superior o un gerente.
Creo que es un buen mapa de ruta y lo llevará a donde lo necesite. Sin conocer los detalles de este proyecto, es difícil ayudarlo mucho. No descarte mi descargo de responsabilidad como excesivamente alarmista. Toneladas de excelentes programadores han batido el polvo al tratar de reescribir un proyecto existente en su idioma favorito o utilizando herramientas "modernas". Esa es una decisión que debe pensarse cuidadosamente y le insto a que no se vuelva pícaro y lo haga por su cuenta sin el apoyo de la gerencia o la asistencia de sus colegas.
fuente
Si prefiere un lenguaje de nivel superior, hay algunos lenguajes como C ++ u Objective-C que se pueden mezclar con código C con bastante facilidad.
Alternativamente, C y C ++ son razonablemente compatibles. Es posible que pueda compilar toda la base de código como C ++ con pocos cambios; tendrá la variable ocasional denominada "clase" o "plantilla" que necesita cambiar de nombre, pero en la práctica eso será todo. (sizeof ('a') es diferente en C y C ++, pero creo que nunca lo he usado).
Si sigue esa ruta, considere que el próximo responsable de mantenimiento podría no ser demasiado fluido con C ++. No te dejes llevar. Aproveche C ++, pero solo hasta el punto en que un programador de C pueda entenderlo fácilmente.
fuente
malloc
) se considera una mala práctica en C. El significado deconst
yinline
también es muy diferente entre C y C ++ y, por supuesto, C ++ no comprende__restrict
. No trate los idiomas como intercambiables, incluso en el subconjunto de fuentes que se compilan en ambos.Básicamente, escribir un buen código C es lo mismo que escribir un buen código C ++ o Java: desea una clase, use a
struct
. Desea herencia, incluya la basestruct
como primer miembro sin nombre. Si desea funciones virtuales, agregue un puntero a una estáticastruct
de punteros de función. Y así sucesivamente, etc. Es exactamente lo que C ++ hace bajo el capó, la única diferencia es que es explícito en C. Por lo tanto, puedes hacer una programación perfectamente orientada a objetos en C, simplemente se ve un poco diferente y más repetitivo de lo que crees. se utilizan para.El punto es que una buena programación se trata de paradigmas, no de características del lenguaje. Es cierto que siempre es bueno si las características de su idioma brindan un buen soporte para los paradigmas que desea usar, pero las características del idioma no son un requisito. Una vez que se dé cuenta de esto, puede escribir un buen código en casi cualquier idioma (aparte de algunos lenguajes esotéricos como brainfuck o INTERCAL, es decir).
Por supuesto, el problema sigue siendo que la biblioteca C estándar no contiene ninguna de esas ingeniosas clases de contenedor a las que está acostumbrado. Desafortunadamente, eso significa que necesitará usar el suyo propio o solucionar esta falta mediante el uso de matrices asignadas dinámicamente. Pero apuesto a que pronto descubrirá que todo lo que realmente necesita son matrices dinámicas (
malloc()
) y listas / árboles vinculados que se implementan a través de miembros de puntero dentro de sus clases.fuente