2010-05-08 14 views
15

Ahora que tengo mi cabeza alrededor del lenguaje 'C' hasta el punto en que me siento lo suficientemente competente como para escribir código limpio, me gustaría centrar mi atención en las pautas de arquitectura del proyecto. Estoy buscando un buen recurso que abarque los siguientes temas:Recomendaciones para 'C' Project Architecture Guidelines?

  1. Cómo crear una interfaz que promueva el mantenimiento del código y sea extensible para futuras actualizaciones.
  2. Directrices para la creación de bibliotecas. Ejemplo, ¿cuándo debería considerar usar bibliotecas estáticas vs dinámicas? Cómo diseñar correctamente un ABI para hacer frente a cualquiera de los dos.
  3. Archivos de encabezado: qué partición y cuándo. Ejemplos sobre cuándo usar 1: 1 frente a 1: muchos .h a .c
  4. Cualquier cosa que sienta que omití pero que es importante cuando intento diseñar un nuevo proyecto en C.

Idealmente, me gustaría ver algunos proyectos de ejemplo que van de pequeños a grandes y ver cómo la arquitectura cambia según el tamaño del proyecto, la función o el cliente.

¿Qué recurso (s) recomendaría usted para tales temas?

Respuesta

10

Cuando comencé a escribir código C en serio, tuve que emular las características de C++. Lo principal que vale la pena hacer es:

  • Considere cada módulo como una clase. Las funciones que expone en el encabezado son como los métodos públicos. Solo coloque una función en el encabezado si forma parte de la interfaz necesaria del módulo.

  • Evite las dependencias de módulos circulares. El Módulo A y el Módulo B no deberían llamarse entre sí. Puede refactorizar algo en un módulo C para evitar eso.

  • De nuevo, siguiendo el patrón C++, si tiene un módulo que puede realizar las mismas operaciones en diferentes instancias de datos, tenga una función crear y eliminar en su interfaz que devolverá un puntero a struct que se devuelve a otras funciones. Pero, por el bien de la encapsulación, devuelve un puntero vacío en la interfaz pública y transfiéralo a tu estructura dentro del módulo.

  • Evite las variables del alcance del módulo: el patrón descrito anteriormente generalmente hará lo que necesite. Pero si realmente necesita variables de ámbito de módulo, agrúpelas en una estructura almacenada en una sola variable de ámbito de módulo denominada "m" o algo consistente. Luego, en su código cada vez que vea "m.variable" sabrá de un vistazo que es una de las estructuras de alcance de módulo.

  • Para evitar problemas en el encabezado, ponga #ifndef MY_HEADER_H #define MY_HEADER_H declaración que protege contra la inclusión doble. El encabezadoh archivo para su módulo, solo debe contener #includes needed FOR THAT HEADER FILE. El archivo .c del módulo puede tener más elementos necesarios para la compilación del módulo, pero no los incluya en el archivo del encabezado del módulo. Esto lo salvará de muchos conflictos de espacios de nombres y problemas de orden de inclusión.

+0

Lista buena. La otra cosa que ver con los encabezados es asegurarse de que cada uno sea independiente: incluye los encabezados que necesita, para que pueda incluirlo sin tener que preocuparse por qué más necesita. –

+2

Re: punteros vacíos en la interfaz pública: prefiero usar estructuras opacas typedef'ed.De esta forma, no tiene que perder el tiempo con el reencasting, y proporciona el mismo tipo de protección de implementación. http://en.wikipedia.org/wiki/Opaque_pointer#C –

+0

De acuerdo con Matt B. No devuelva el vacío *, use tipos de datos abstractos tipo typedef'd. Vea mi ejemplo aquí en la pregunta "Implemente una clase en su idioma favorito": http://stackoverflow.com/questions/2702450/implement-a-simple-class-in-your-favorite-language/2771898#2771898 –

3
  1. Código de presentación separado de la lógica. Eso es muy importante.
  2. Estático si se usan solo en su proyecto o en algunos binarios, dinámico cuando se usa muchas veces (ahorra mucho espacio).
  3. Cuando el código se use más de una vez, divídalo en un encabezado.

Esos son algunos de mis consejos conocidos que puedo darle.

4

Limpieza del espacio de nombres: especialmente importante en las bibliotecas. Prefija sus funciones públicas con el nombre de la biblioteca, de alguna manera. Si su biblioteca se llamó 'happyland', realice funciones como "happyland_init" o incluso "hl_init".

Esto va para el uso de la estática. Escribirás funciones que son especializadas: ocúlpalas de otros módulos usando estática libremente.

Para las bibliotecas, el reentrante también es crítico. No dependa del estado global que no esté compartimentado (puede tener un token de struct typedef para mantener este estado si es necesario).

6

Las verdades acerca de los sistemas de arquitectura son intemporales y cruzan los límites del lenguaje. Aquí hay un pequeño consejo centrado en C:

  • Cada módulo esconde un secreto. Construya su sistema alrededor de interfaces que ocultan información de sus clientes. El único constructo oculto de información de tipo C de C es el puntero a una estructura incompleta. Aprende a fondo y úsalo a menudo.

  • Una interfaz para una implementación es una buena regla empírica. (La interfaz es .h, la implementación es .c). A veces querrá proporcionar dos interfaces que se relacionan con la misma implementación: una que oculta la representación y otra que la expone.

  • Necesitará convenciones de nomenclatura.

Un modelo excelente de cómo manejar este tipo de problemas en C es de Dave Hanson C Interfaces and Implementations. En él, verá cómo diseñar buenas interfaces e implementaciones, y cómo una interfaz puede construir sobre otra para formar una biblioteca coherente. También obtendrá un excelente conjunto de interfaces de inicio que puede usar en sus propias aplicaciones. Para alguien en su posición, No puedo recomendar demasiado este libro. Es un arquetipo de los sistemas de buena arquitectura en C.

+0

+1 para CII. Muy buen libro, en más de un sentido. –

3

Cómo crear una interfaz que promueve código de mantenimiento y es extensible para futuras actualizaciones.

Exponiendo los menores detalles de implementación posibles. Por ejemplo,

  • Use opaque pointer s.
  • Si necesita una función "privada", declare static, y no la coloque en el archivo .h.