2009-07-22 19 views

Respuesta

0

Hay directorios y archivos, pero no espacios de nombres ni encapsulamiento. Puede compilar cada módulo en un archivo obj separado y vincularlos (como bibliotecas).

+0

¿qué ocurre con la creación de DLL que contienen funcionalidades relacionadas? –

+0

Una DLL es un tipo de biblioteca. –

1

Romper el código en bibliotecas de funciones relacionadas es una forma de mantener las cosas organizadas. Para evitar conflictos de nombres, también puede usar prefijos para permitirle reutilizar los nombres de las funciones, aunque con buenos nombres nunca he encontrado realmente que esto sea un gran problema. Por ejemplo, si desea desarrollar sus propias rutinas matemáticas pero todavía utiliza algunas de la biblioteca matemática estándar, puede prefijar las suyas con alguna cadena: xyz_sin(), xyz_cos().

Generalmente prefiero la función (o conjunto de funciones estrechamente relacionadas) por archivo y un archivo de encabezado por convención de archivo de origen. También es una buena idea dividir los archivos en directorios, donde cada directorio representa una biblioteca separada. En general, tendrías un sistema de makefiles o archivos de compilación que te permitirían construir todo o parte de todo el sistema siguiendo la jerarquía que representa las diversas bibliotecas/programas.

+0

+1, pero use un único archivo Makefile que recursivamente # incluya archivos Makefile.inc en subdirectorios, ya que el archivo make recursivo tradicional puede romper sutilmente las dependencias de compilación (google "Makefiles recursivos considerados dañinos") –

15

Crea archivos de encabezado que contienen SOLAMENTE lo que es necesario para usar un módulo. En el (los) archivo (s) .c correspondiente, haga que todo lo que no esté destinado a ser visible fuera (por ejemplo, funciones de ayuda) estético. Use prefijos en los nombres de todo lo visible externamente para ayudar a evitar colisiones de espacios de nombres. (Si un módulo abarca múltiples archivos, las cosas se vuelven más difíciles., Ya que puede necesitar exponer cosas internas y no poder ocultarlas con "estática")

(Si tuviera que tratar de mejorar C, una cosa que haría hacer es "estático" el alcance predeterminado de las funciones. Si quisiera algo visible afuera, tendría que marcarlo con "exportar" o "global" o algo similar.)

+2

+1. El uso de funciones estáticas para el comportamiento "privado" limita el potencial de acoplamiento innecesario. –

+2

Asegúrese de que el encabezado funciona de forma aislada utilizándolo como el primer encabezado enumerado en el archivo de implementación. Si eso no funciona, el encabezado está incompleto. –

+0

Pensé que static * was * el alcance predeterminado. Al menos eso es lo que recuerdo haber leído en k & r. – Breton

10

OO se pueden aplicar técnicas a C código, solo requieren más disciplina.

  • Uso asas opacos que operan sobre los objetos. Un buen ejemplo de cómo se hace esto es la biblioteca stdio: todo está organizado alrededor del asa opaca FILE*. Muchas bibliotecas exitosas se organizan en torno a este principio (por ejemplo zlib, apr)
  • Debido a que todos los miembros de struct s son implícitamente public en C, se necesita una disciplina convención + programador para hacer cumplir la técnica útil de ocultación de la información. Elija una convención simple y automáticamente seleccionable como "miembros privados terminan con '_'".
  • Las interfaces se pueden implementar usando matrices de punteros para las funciones. Ciertamente, esto requiere más trabajo que en lenguajes como C++ que proporcionan apoyo en el lenguaje, pero sin embargo se puede hacer en C.
+2

manijas opacas es una buena. Y aunque las estructuras son implícitamente públicas, solo si las pones en el encabezado. Puede decir, en un encabezado de usuario para una biblioteca, por ejemplo: [código] struct opaque_foo; extern int my_func (struct opaque_foo * f); [/ code] El código interno de la biblioteca, por supuesto, deletrearía lo que estaba en opaque_foo, pero no es necesario exponerlo al código que está utilizando la biblioteca. – smcameron

2

El enfoque que Pidgin (antes Gaim) utiliza es que crearon una estructura Plugin. Cada complemento rellena una estructura con devoluciones de llamadas para la inicialización y el desmontaje, junto con una gran cantidad de otra información descriptiva. Prácticamente todo, excepto la estructura, se declara estática, por lo que solo se expone la estructura del complemento para el enlace.

Luego, para manejar el acoplamiento flojo del complemento que se comunica con el resto de la aplicación (ya que sería bueno si hizo algo entre la configuración y el desmontaje), tienen un sistema de señalización.Los complementos pueden registrar devoluciones de llamadas para llamar cuando se emiten señales específicas (no señales C estándar, sino un tipo extensible personalizado [identificado por cadena, en lugar de establecer códigos]) por parte de la aplicación (incluido otro complemento). También pueden emitir señales ellos mismos.

Esto parece funcionar bien en la práctica: los diferentes complementos se pueden construir unos sobre otros, pero el acoplamiento es bastante flexible, no hay invocación directa de funciones, todo se realiza a través del sistema de señalización.

+0

+1. Esta es una forma muy extensible de diseñar un sistema. Las interfaces (tablas de devoluciones de llamada) mantienen el acoplamiento al mínimo, lo que facilita intercambiar implementaciones nuevas/mejoradas para diferentes componentes * por separado * en el futuro. –

3
  1. No definir variables en archivos de encabezado; en su lugar, defina la variable en el archivo fuente y agregue una declaración externa (declaración) en el encabezado. Esto se vinculará con los números 2 y 3.
  2. Utilice un protector de inclusión en cada encabezado. Esto ahorrará muchos dolores de cabeza.
  3. Suponiendo que ha hecho los números 1 y 2, incluya todo lo que necesita (pero solo lo que necesita) para un determinado archivo en ese archivo. No dependa del orden de cómo el compilador expande sus directivas de inclusión.
+0

+1 para #include guardias. –

+0

Hay un buen truco en un apéndice del libro de Andrew Glassner "Graphics Gems" en el que tiene algunas macros que le permiten definir una declaración de variable y su declaración externa (y el inicializador) en una sola línea de código en el encabezado. Esto no permite que el archivo de implementación * .c use el mismo encabezado que el código de cliente. Ejemplo de algunos de mis códigos: http://gneutronica.cvs.sourceforge.net/viewvc/gneutronica/gneutronica/midioutput_alsa.h?revision=1.3&view=markup Mire las macros GLOBAL e INIT, y cómo están condicionalmente definido. – smcameron

2

Una función debe hacer una cosa y hacer esto bien.

Muchas funciones pequeñas utilizadas por las funciones de envoltura más grandes ayudan a estructurar el código desde bloques de construcción pequeños, fáciles de entender (¡y probar!).

Cree pequeños módulos con un par de funciones cada uno. Solo exponga lo que debe, mantenga todo lo demás estático dentro del módulo. Enlace pequeños módulos junto con sus archivos de interfaz .h

Proporcionan funciones Getter y Setter para acceder a variables de alcance de archivos estáticos en su módulo. De esta forma, las variables solo se escriben en un solo lugar. Esto también ayuda a rastrear el acceso a estas variables estáticas utilizando un punto de interrupción en la función y la pila de llamadas.

Una regla importante al diseñar código modular es: No intente optimizar a menos que sea necesario. Muchas funciones pequeñas generalmente producen un código más limpio y bien estructurado, y la sobrecarga de llamada de función adicional puede valer la pena.

Siempre trato de mantener las variables en su ámbito más estrecho, también dentro de las funciones. Por ejemplo, los índices de bucles for usualmente pueden mantenerse en el alcance del bloque y no necesitan estar expuestos en todo el nivel de función. C no es tan flexible como C++ con "defínalo donde lo usa", pero es factible.

Cuestiones relacionadas