¿Puedo escribir código C ++ sin encabezados (declaraciones de funciones repetitivas)?

¿Hay alguna forma de no tener que escribir declaraciones de funciones dos veces (encabezados) y aún conservar la misma escalabilidad en la comstackción, la claridad en la depuración y la flexibilidad en el diseño cuando se progtwig en C ++?

Usa Lzz . Toma un solo archivo y crea automáticamente .h y .cpp para usted con todas las declaraciones / definiciones en el lugar correcto.

Lzz es realmente muy poderoso y maneja el 99% de la syntax completa de C ++, incluidas plantillas, especializaciones, etc. etc., etc.

Actualización 150120:

La syntax más nueva de C ++ ’11 / 14 solo se puede usar dentro de los cuerpos de la función Lzz.

Sentí lo mismo cuando comencé a escribir C, así que también investigué esto. La respuesta es que sí, es posible y no, no quieres.

Primero con el sí

En GCC, puedes hacer esto:

// foo.cph void foo(); #if __INCLUDE_LEVEL__ == 0 void foo() { printf("Hello World!\n"); } #endif 

Esto tiene el efecto deseado: combina encabezado y fuente en un archivo que puede incluirse y vincularse.

Luego con el no:

Esto solo funciona si el comstackdor tiene acceso a la fuente completa. No puede usar este truco al escribir una biblioteca que desea distribuir, pero sí mantener el código cerrado. O bien distribuye el archivo .cph completo, o tiene que escribir un archivo .h por separado para ir con su .lib. Aunque tal vez podría autogenerarlo con el macroprocesador. Sin embargo, se pondría peludo.

Y razone # 2 por qué no quiere esto, y esa es probablemente la mejor: velocidad de comstackción . Normalmente, los archivos de fonts C solo deben recomstackrse cuando el archivo cambia, o cambia cualquiera de los archivos que incluye.

  • El archivo C puede cambiar con frecuencia, pero el cambio solo implica volver a comstackr el archivo que cambió.
  • Los archivos de encabezado definen las interfaces, por lo que no deberían cambiar con tanta frecuencia. Sin embargo, cuando lo hacen, desencadenan una recomstackción de cada archivo fuente que los incluye.

Cuando todos sus archivos se combinan en los archivos de encabezado y fuente, cada cambio desencadenará una recomstackción de todos los archivos fuente. C ++ no es conocido por sus rápidos tiempos de comstackción incluso ahora, imagine lo que sucedería cuando todo el proyecto tuviera que ser recomstackdo cada vez. Luego, extrapole eso a un proyecto de cientos de archivos fuente con dependencias complicadas …

Lo sentimos, pero no existe la “mejor práctica” para eliminar encabezados en C ++: es una mala idea, punto. Si los odias tanto, tienes tres opciones:

  • Familiarícese íntimamente con las partes internas de C ++ y cualquier comstackdor que esté utilizando; te encontrarás con problemas diferentes a los del desarrollador promedio de C ++, y probablemente tendrás que resolverlos sin mucha ayuda.
  • Elija un idioma que pueda usar “a la derecha” sin deprimirse
  • Obtenga una herramienta para generarlos para usted; todavía tendrá encabezados, pero ahorrará algo de esfuerzo de tipeo

En su artículo Soporte simple para diseño por contrato en C ++ , Pedro Guerreiro afirmó:

Por lo general, una clase de C ++ viene en dos archivos: el archivo de encabezado y el archivo de definición. ¿Dónde deberíamos escribir las aserciones: en el archivo de encabezado, porque las aserciones son especificación? O en el archivo de definición, ya que son ejecutables? ¿O en ambos, corriendo el riesgo de incoherencia (y duplicar el trabajo)? En cambio, recomendamos que abandonemos el estilo tradicional y eliminemos el archivo de definición, usando solo el archivo de encabezado, como si todas las funciones estuvieran definidas en línea, de manera muy similar a lo que hacen Java y Eiffel.

Este es un cambio tan drástico de la normalidad de C ++ que corre el riesgo de matar el esfuerzo desde el principio. Por otro lado, mantener dos archivos para cada clase es tan incómodo, que tarde o temprano surgirá un entorno de desarrollo en C ++ que nos lo oculta, lo que nos permite concentrarnos en nuestras clases, sin tener que preocuparnos por dónde están almacenadas.

Eso fue en 2001. Estuve de acuerdo. Ahora es 2009 y todavía no ha surgido “un entorno de desarrollo que nos lo oculte, permitiéndonos concentrarnos en nuestras clases”. En cambio, los tiempos de comstackción largos son la norma.


Nota: El enlace de arriba parece estar muerto ahora. Esta es la referencia completa de la publicación, tal como aparece en la sección de Publicaciones del sitio web del autor:

Pedro Guerreiro, Soporte simple para diseño por contrato en C ++ , TOOLS USA 2001, Proceedings, páginas 24-34, IEEE, 2001.

No hay una forma práctica de evitar los encabezados. Lo único que podrías hacer es poner todo el código en un gran archivo de c ++. Eso terminará en un lío inmanejable, así que por favor no lo hagas.

Por el momento, los archivos de encabezado C ++ son un mal necesario. No me gustan, pero no hay forma de evitarlos. Sin embargo, me encantaría ver algunas mejoras e ideas frescas sobre el problema.

Por cierto, una vez que te hayas acostumbrado ya no es tan malo. C ++ (y cualquier otro idioma también) tiene cosas más molestas.

Lo que he visto que algunas personas como tú hacen es escribir todo en los encabezados . Eso le da a su propiedad deseada de tener que escribir los perfiles del método una sola vez.

Personalmente, creo que hay muy buenas razones por las cuales es mejor separar la statement y la definición, pero si esto te angustia, hay una manera de hacer lo que quieres.

Debe escribir la statement de función dos veces, en realidad (una vez en el archivo de encabezado, una vez en el archivo de implementación). La definición (implementación AKA) de la función se escribirá una vez, en el archivo de implementación.

Puede escribir todo el código en archivos de encabezado (en realidad es una práctica muy utilizada en progtwigción genérica en C ++), pero esto implica que cada archivo C / CPP que incluye ese encabezado implicará la recomstackción de la implementación de esos archivos de encabezado.

Si está pensando en un sistema similar a C # o Java, no es posible en C ++.

Hay un software de generación de archivos de cabecera. Nunca lo he usado, pero valdría la pena investigarlo. Por ejemplo, echa un vistazo a mkhdr ! Supuestamente escanea los archivos C y C ++ y genera los archivos de encabezado apropiados.

(Sin embargo, como señala Richard, esto parece limitar el uso de cierta funcionalidad de C ++. Ver la respuesta de Richard en su lugar aquí en este hilo ).

En realidad … Puedes escribir toda la implementación en un archivo. Las clases con plantilla están todas definidas en el archivo de encabezado sin archivo cpp.

También puede guardar con las extensiones que desee. Luego, en las declaraciones #include, incluirías tu archivo.

 /* mycode.cpp */ #pragma once #include  class myclass { public: myclass(); dothing(); }; myclass::myclass() { } myclass::dothing() { // code } 

Luego en otro archivo

 /* myothercode.cpp */ #pragma once #include "mycode.cpp" int main() { myclass A; A.dothing(); return 0; } 

Es posible que deba configurar algunas reglas de comstackción, pero debería funcionar.

Nadie ha mencionado Visual-Assist X en Visual Studio 2012 todavía.

Tiene un montón de menús y teclas rápidas que puede usar para aliviar el dolor de mantener encabezados:

  • “Crear statement” copia la statement de función de la función actual en el archivo .hpp.
  • “Refactor .. Cambiar firma” le permite actualizar simultáneamente el archivo .cpp y .h con un solo comando.
  • Alt-O le permite alternar instantáneamente entre archivos .cpp y .h.

Puedes evitar encabezados. Completamente. Pero no lo recomiendo

Te enfrentarás a algunas limitaciones muy específicas. Una de ellas es que no podrá tener referencias circulares (no podrá tener la clase Parent con un puntero a una instancia de la clase ChildNode, y la clase ChildNode también contiene un puntero a una instancia de la clase Parent. tendrían que ser uno o el otro.)

Existen otras limitaciones que simplemente hacen que tu código sea realmente extraño. Quédate con los encabezados Aprenderá a simpatizar con ellos (ya que ofrecen una buena y rápida sinopsis de lo que una clase puede hacer).

Para ofrecer una variante de la popular respuesta de rix0rrr:

 // foo.cph #define INCLUDEMODE #include "foo.cph" #include "other.cph" #undef INCLUDEMODE void foo() #if !defined(INCLUDEMODE) { printf("Hello World!\n"); } #else ; #endif void bar() #if !defined(INCLUDEMODE) { foo(); } #else ; #endif 

No lo recomiendo, creo que esta construcción demuestra la eliminación de la repetición de contenido a costa de la repetición mecánica. ¿Supongo que hace más fácil la copia de pasta? Eso no es realmente una virtud.

Al igual que con todos los otros trucos de esta naturaleza, una modificación en el cuerpo de una función aún requerirá la recomstackción de todos los archivos, incluido el archivo que contiene esa función. Las herramientas automáticas muy cuidadosas pueden evitar esto en parte, pero aún tendrían que analizar el archivo de origen para verificarlo, y deben construirse cuidadosamente para no reescribir su resultado si no es diferente.

Para otros lectores: pasé unos minutos tratando de descubrir incluir guardias en este formato, pero no se me ocurrió nada bueno. ¿Comentarios?

Después de leer todas las otras respuestas, me parece que falta que haya trabajo continuo para agregar soporte para los módulos en el estándar C ++. No llegará a C ++ 0x, pero la intención es que se aborde en una revisión técnica posterior (en lugar de esperar a un nuevo estándar, que llevará años).

La propuesta que se estaba discutiendo es N2073 .

La parte mala de esto es que no lo conseguirás, ni siquiera con los comstackdores c ++ 0x más nuevos. Tendrás que esperar. Mientras tanto, deberá comprometerse entre la exclusividad de las definiciones en las bibliotecas de solo encabezado y el costo de la comstackción.

Hasta donde yo sé, no. Los encabezados son una parte inherente de C ++ como un lenguaje. No olvide que la statement directa le permite al comstackdor simplemente incluir un puntero de función a un objeto / función comstackdo sin tener que incluir la función completa (que puede obtener al declarar una función en línea (si el comstackdor así lo desea).

Si realmente, realmente, realmente odias hacer encabezados, escribe una secuencia de comandos perl para autogenerarlos, en su lugar. No estoy seguro de que lo recomiende.

Entiendo tus problemas Diría que el problema principal de C ++ es el método de comstackción / comstackción que heredó de C. La estructura del encabezado C / C ++ se ha diseñado en momentos en que la encoding implicaba menos definiciones y más implementaciones. No me tires botellas, pero así es como se ve.

Desde entonces, el OOP ha conquistado el mundo y el mundo es más acerca de las definiciones y las implementaciones. Como resultado, incluir encabezados resulta bastante doloroso para trabajar con un lenguaje en el que las colecciones fundamentales, como las de STL, se crean con plantillas que son un trabajo notoriamente difícil para el comstackdor. Toda esa magia con los encabezados precomstackdos no ayuda mucho cuando se trata de TDD, herramientas de refactorización, el entorno de desarrollo general.

Por supuesto, los progtwigdores de C no sufren demasiado porque no tienen archivos de encabezado pesados ​​para el comstackdor y por lo tanto están contentos con la cadena de herramientas de comstackción bastante sencilla y de bajo nivel. Con C ++, esta es una historia de sufrimiento: declaraciones interminables, encabezados precomstackdos, analizadores externos, preprocesadores personalizados, etc.

Muchas personas, sin embargo, no se dan cuenta de que C ++ es el ÚNICO lenguaje que tiene soluciones fuertes y modernas para problemas de alto y bajo nivel. Es fácil decir que debe buscar otro idioma con el sistema de construcción y reflexión adecuado, pero no tiene sentido que tengamos que sacrificar las soluciones de progtwigción de bajo nivel con eso y tenemos que complicar las cosas con un lenguaje de bajo nivel mezclado con alguna solución basada en máquina virtual / JIT.

Tengo esta idea desde hace un tiempo, que sería lo mejor de la tierra tener una cadena de herramientas c ++ basada en “unidades”, similar a la de D. El problema surge con la parte multiplataforma: el objeto los archivos pueden almacenar cualquier información, no hay problema con eso, pero dado que en Windows la estructura del archivo de objeto es diferente a la del ELF, sería una molestia implementar una solución multiplataforma para almacenar y procesar la mitad del camino unidades de comstackción

Es completamente posible desarrollar sin archivos de encabezado. Uno puede incluir un archivo fuente directamente:

 #include "MyModule.c" 

El principal problema con esto es una de dependencias circulares (es decir: en C debe declarar una función antes de llamarla). Esto no es un problema si diseñas tu código completamente de arriba hacia abajo, pero puede llevarte algo de tiempo ajustar tu cabeza a este tipo de patrón de diseño si no estás acostumbrado.

Si absolutamente debe tener dependencias circulares, puede considerar crear un archivo específicamente para declaraciones e incluirlo antes que cualquier otra cosa. Esto es un poco inconveniente, pero aún menos contaminado que tener un encabezado para cada archivo C.

Actualmente estoy desarrollando el uso de este método para uno de mis proyectos principales. Aquí hay un desglose de las ventajas que he experimentado:

  • Mucho menos contaminación de archivos en su árbol fuente.
  • Tiempos de construcción más rápidos. (El comstackdor solo produce un archivo de objeto, main.o)
  • Archivos de creación más simples. (El comstackdor solo produce un archivo de objeto, main.o)
  • No es necesario “hacer limpieza”. Cada construcción es “limpia”.
  • Menos código de placa de caldera. Menos código = menos errores potenciales.

Descubrí que Gish (un juego de Cryptic Sea, Edmund McMillen) usaba una variación de esta técnica dentro de su propio código fuente.

Puede diseñar cuidadosamente sus funciones para que todas las funciones dependientes se compilen después de sus dependencias, pero como implicó Nils, eso no es práctico.

Catalin (perdone las marcas diacríticas que faltan) también sugirió una alternativa más práctica para definir sus métodos en los archivos de encabezado. En realidad, esto puede funcionar en la mayoría de los casos … especialmente si tiene guardias en los archivos de encabezado para asegurarse de que solo se incluyan una vez.

Personalmente, creo que los archivos de cabecera + funciones de statement es mucho más deseable para ‘dar con la cabeza’ el nuevo código, pero eso es una preferencia personal, supongo …

Puedes prescindir de los encabezados. Pero, ¿por qué gastar esfuerzos tratando de evitar cuidadosamente las mejores prácticas desarrolladas durante muchos años por expertos?

Cuando escribí básico, me gustaron bastante los números de línea. Pero, no pensaría en tratar de meterlos en C ++, porque esa no es la forma C ++. Lo mismo aplica para los encabezados … y estoy seguro de que otras respuestas explican todo el razonamiento.

Para fines prácticos , no, no es posible. Técnicamente, sí, puedes. Pero, francamente, es un abuso del idioma, y ​​debe adaptarse al idioma. O muévete a algo como C #.

Es una buena práctica usar los archivos de encabezado, y después de un tiempo crecerá en ti. Estoy de acuerdo en que tener solo un archivo es más fácil, pero también puede afectar el mal cifrado.

Algunas de estas cosas, aunque se sienten incómodas, le permiten obtener más de lo que parece.

como ejemplo, piense en punteros, pase parámetros por valor / por referencia … etc.

para mí, los archivos de encabezado me permiten mantener mis proyectos estructurados correctamente

Aprenda a reconocer que los archivos de encabezado son algo bueno. Separan cómo se le aparecen los códigos a otro usuario desde la implementación de cómo realmente realiza sus operaciones.

Cuando uso el código de alguien, ahora quiero tener que recorrer toda la implementación para ver cuáles son los métodos en una clase. Me importa lo que hace el código, no cómo lo hace.

Esto ha sido “revivido” gracias a un duplicado …

En cualquier caso, el concepto de encabezado es valioso, es decir, separa la interfaz del detalle de implementación. El encabezado describe cómo usar una clase / método, y no cómo lo hace.

La desventaja es el detalle dentro de los encabezados y todas las soluciones necesarias. Estos son los principales problemas según los veo:

  • generación de dependencia Cuando se modifica un encabezado, cualquier archivo fuente que incluya este encabezado requiere una recomstackción. El problema es, por supuesto, averiguar qué archivos fuente realmente lo usan. Cuando se realiza una comstackción “limpia”, a menudo es necesario almacenar en caché la información en algún tipo de árbol de dependencias para más adelante.

  • incluye guardias. Ok, todos sabemos cómo escribir estos pero en un sistema perfecto no sería necesario.

  • detalles privados. Dentro de una clase, debe poner los detalles privados en el encabezado. Sí, el comstackdor necesita saber el “tamaño” de la clase, pero en un sistema perfecto sería capaz de unir esto en una fase posterior. Esto lleva a todo tipo de soluciones como pImpl y el uso de clases base abstractas, incluso cuando solo tiene una implementación solo porque quiere ocultar una dependencia.

El sistema perfecto funcionaría con

  • statement de clase separada y statement
  • Un enlace claro entre estos dos para que el comstackdor sepa dónde está una statement de clase y su definición, y sabría cuál es el tamaño de una clase.
  • Usted declara using class lugar de preprocesador #include . El comstackdor sabe dónde encontrar una clase. Una vez que hayas hecho “usar clase”, puedes usar ese nombre de clase sin calificarlo.

Me interesaría saber cómo lo hace D.

Con respecto a si puede usar C ++ sin encabezados, diría que no, los necesita para clases base abstractas y biblioteca estándar. Aparte de eso, podrías vivir sin ellos, aunque probablemente no quieras.