¿Por qué C++ no tiene reflexión?


Esta es una pregunta algo extraña. Mis objetivos son entender la decisión de diseño del lenguaje e identificar las posibilidades de reflexión en C++.

  1. ¿Por qué el comité del lenguaje C++ no fue hacia la implementación de la reflexión en el lenguaje? ¿Es la reflexión demasiado difícil en un lenguaje que no se ejecuta en una máquina virtual (como java)?

  2. Si se implementara la reflexión para C++, ¿cuáles serían los desafíos?

Supongo que los usos de reflexión son bien conocidos: los editores se pueden escribir más fácilmente, el código del programa será más pequeño, se pueden generar burlas para pruebas unitarias, etc. Pero sería genial si pudieras comentar sobre los usos de la reflexión también.

Author: amit_grepclub, 2008-12-11

14 answers

Hay varios problemas con la reflexión en C++.

  • Es mucho trabajo agregar, y el comité de C++ es bastante conservador, y no invierta tiempo en nuevas características radicales a menos que esté seguro de que valdrá la pena. (Se ha hecho una sugerencia para agregar un sistema de módulos similar a los ensamblados. NET, y aunque creo que hay un consenso general de que sería bueno tenerlo, no es su principal prioridad en este momento, y se ha pospuesto hasta mucho después de C++0x. la motivación para esta característica es deshacerse del sistema #include, pero también habilitaría al menos algunos metadatos).

  • No pagas por lo que no pagas utilizar. Esa es una de las necesidades básicas filosofías de diseño subyacentes a C++. ¿Por qué debería llevar mi código? metadatos si es posible que nunca los necesite? Además, la adición de metadatos puede inhibir al compilador de optimizar. ¿Por qué debería pagar eso? costo en mi código si es posible que nunca necesite los metadatos?

  • Que nos lleva a otro gran punto: C++ hace muy pocas garantías sobre el código compilado. El compilador se le permite hacer bastante mucho cualquier cosa que le guste, siempre y cuando la funcionalidad resultante es lo que se espera. Por ejemplo, su las clases no están obligadas a estar allí. El compilador puede optimizarlos lejos, en línea todo lo que hacen, y con frecuencia hace precisamente eso, porque incluso el código de plantilla simple tiende a crear un buen número de plantilla instanciaciones. El estándar C++ biblioteca se basa en este agresivo optimización. Los funtores son solamente performant si la sobrecarga de creación de instancias y destrucción de la el objeto se puede optimizar lejos. operator[] en un vector solo es comparable a raw indexación de matrices en rendimiento porque todo el operador puede ser entre líneas y por lo tanto eliminado completamente del código compilado. C# y Java hacer un montón de garantías sobre el salida del compilador. Si defino una clase en C#, entonces esa clase existe en el resultado Asamblea. Incluso si nunca lo uso. Incluso si todos las llamadas a sus funciones miembro podrían estar en línea. La clase tiene que ser allí, para que la reflexión pueda encontrar se. Parte de esto se alivia por C# compilar a bytecode, lo que significa que el compilador JIT puede eliminar definiciones de clase y en línea funciones si le gusta, incluso si el el compilador inicial de C# no puede. En C++, solo tienes un compilador, y tiene que generar código eficiente. Si se les permitió inspeccionar los metadatos de un ejecutable de C++ , esperarías que ver cada clase que definió, que significa que el compilador tendría para conservar todas las clases definidas, incluso si no son necesarios.

  • Y luego están las plantillas. Las plantillas en C++ no se parecen en nada a genéricos en otros idiomas. Cada plantilla instanciación crea un nuevo tipo. std::vector<int> es una clase completamente separada de std::vector<float>. Eso se suma a muchos tipos diferentes en un todo programa. ¿Cuál debe ser nuestra reflexión ¿ves? La plantilla std::vector? Pero ¿cómo puede, ya que es un construcción de código fuente, que no tiene ¿significa en tiempo de ejecución? Tendría que ver las clases separadas std::vector<int> y std::vector<float>. Y std::vector<int>::iterator y std::vector<float>::iterator, lo mismo para const_iterator y así sucesivamente. Y una vez que entras en la plantilla metaprogramación, usted termina rápidamente para arriba creación de instancias de cientos de plantillas, todos los cuales se insertan y se eliminan de nuevo por el compilador. No tienen significado, excepto como parte de un metaprograma en tiempo de compilación. Todos deben estos cientos de clases son visibles ¿a reflexionar? Ellos tengo que, porque de lo contrario nuestra reflexión sería inútil, si ni siquiera garantiza que las clases que definí estarán realmente allí . Y un problema secundario es que la clase de plantilla no existe hasta que se instancie. Imagine un programa que usa std::vector<int>. ¿Debería nuestro sistema de reflexión ser capaz de ver std::vector<int>::iterator? Por un lado, es de esperar. Es una clase importante, y se define en términos de std::vector<int>, que existe en los metadatos. Por otra parte, si el el programa en realidad nunca utiliza esta clase iterator plantilla, su tipo nunca ha sido creada, y por lo que el compilador no han generado la clase en el primer lugar. Y es demasiado tarde para crearlo en tiempo de ejecución, ya que requiere acceso al código fuente.

  • Y finalmente, la reflexión no es del todo tan vital en C++ como en C#. El la razón es otra vez, plantilla metaprogramación. No puede resolver todo, pero para muchos casos donde de lo contrario, recurrirías a reflexión, es posible escribir un metaprograma que hace lo mismo cosa en tiempo de compilación. boost::type_traits es un simple ejemplo. Quieres saber sobre el tipo T? Compruebe su type_traits. En C#, tendrías que pescar por ahí después de su escriba usando reflexión. Reflexión todavía sería útil para algunos cosas (el uso principal que puedo ver, qué metaprogramación no puede fácilmente reemplazar, es para autogenerado código de serialización), pero sería llevar algunos costos significativos para C++, y simplemente no es necesario tan a menudo como lo es en otros idioma.

Editar: En respuesta a las observaciones:

Cdleary: Sí, los símbolos de depuración hacen algo similar, ya que almacenan metadatos sobre los tipos utilizados en el ejecutable. Pero también sufren de los problemas que describí. Si alguna vez has intentado depurar una versión, sabrás a qué me refiero. Hay grandes espacios lógicos donde se creó una clase en el código fuente, que se ha insertado en el código final. Si tuvieras que usar la reflexión para algo útil, lo necesitarías para ser más confiable y consistente. Tal como está, los tipos desaparecerían y desaparecerían casi cada vez que compile. Cambias un pequeño detalle, y el compilador decide cambiar qué tipos se insertan y cuáles no, como respuesta. ¿Cómo se extrae algo útil de eso, cuando ni siquiera se garantiza que los tipos más relevantes se representarán en sus metadatos? El tipo que estaba buscando puede haber estado allí en la última compilación, pero ahora es ido. Y mañana, alguien comprobará un pequeño cambio inocente a una pequeña función inocente, lo que hace que el tipo sea lo suficientemente grande como para que no quede completamente alineado, por lo que volverá de nuevo. Eso sigue siendo útil para los símbolos de depuración, pero no mucho más que eso. Odiaría intentar generar código de serialización para una clase bajo esos términos.

Evan Teran: Por supuesto que estos problemas podrían resolverse. Pero eso vuelve a mi punto # 1. Tomaría mucho trabajo, y el C++ comité tiene un montón de cosas que sienten que es más importante. ¿Es el beneficio de obtener una reflexión limitada (y sería limitada) en C++ lo suficientemente grande como para justificar enfocarse en eso a expensas de otras características? ¿Hay realmente un gran beneficio en agregar características del lenguaje principal que ya se pueden (principalmente) hacer a través de bibliotecas y preprocesadores como QT? Tal vez, pero la necesidad es mucho menos urgente que si tales bibliotecas no existieran. Para sus sugerencias específicas, sin embargo, yo creo que rechazarlo en las plantillas lo haría completamente inútil. Por ejemplo, no podría usar reflexión en la biblioteca estándar. ¿Qué tipo de reflexión no te dejaría ver un std::vector? Las plantillas son una enorme parte de C++. Una característica que no funciona en plantillas es básicamente inútil.

Pero tienes razón, se podría implementar alguna forma de reflexión. Pero sería un cambio importante en el idioma. Como es ahora, los tipos son exclusivamente una construcción en tiempo de compilación. Existen para el beneficio del compilador, y nada más. Una vez compilado el código, no hay clases. Si te estiras, podrías argumentar que las funciones todavía existen, pero en realidad, todo lo que hay es un montón de instrucciones de ensamblador de salto,y un montón de push/pop de pila.

Pero como he dicho, hay una propuesta de cambios en el modelo de compilación, agregar módulos autónomos, almacenar metadatos para tipos seleccionados, permitir que otros módulos hagan referencia a ellos sin tener que meterse con #include s. Eso es un buen comienzo, y para ser honesto, me sorprende que el comité de estándares no solo desechara la propuesta por ser un cambio demasiado grande. Así que tal vez en 5-10 años? :)

 583
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-11 09:42:21

La reflexión requiere que algunos metadatos sobre los tipos se almacenen en algún lugar que se pueda consultar. Dado que C++ compila a código máquina nativo y sufre grandes cambios debido a la optimización, la vista de alto nivel de la aplicación se pierde en el proceso de compilación, por lo tanto, no será posible consultarlos en tiempo de ejecución. Java y. NET utilizan una representación de muy alto nivel en el código binario para máquinas virtuales haciendo posible este nivel de reflexión. En algunas implementaciones de C++ , sin embargo, hay algo llamado Información de Tipo de Tiempo de ejecución (RTTI) que puede considerarse una versión simplificada de reflexión.

 37
Author: Mehrdad Afshari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-12 07:02:16

Todos los idiomas no deben tratar de incorporar todas las características de cualquier otro idioma.

C++ es esencialmente un ensamblador de macro muy, muy sofisticado. NO es (en un sentido tradicional) un lenguaje de alto nivel como C#, Java, Objective-C, Smalltalk, etc.

Es bueno tener diferentes herramientas para diferentes trabajos. Si solo tenemos martillos, todas las cosas van a parecer clavos, etc. Tener lenguajes de script es útil para algunos trabajos, y lenguajes OO reflexivos (Java, Obj-C, C#) son útiles para otra clase de trabajos, y los lenguajes básicos súper eficientes cercanos a la máquina son útiles para otra clase de trabajos (C++, C, Assembler).

C++ hace un trabajo increíble al extender la tecnología de Ensamblador a niveles increíbles de gestión de complejidad y abstracciones para hacer que la programación de tareas más grandes y complejas sea mucho más posible para los seres humanos. Pero no es necesariamente un idioma que es el más adecuado para aquellos que se acercan a su problema de una perspectiva estrictamente de alto nivel (Lisp, Smalltalk, Java, C#). Si necesita un idioma con esas características para implementar mejor una solución a sus problemas, ¡agradezca a aquellos que han creado tales idiomas para que todos nosotros los usemos!

Pero C++ es para aquellos que, por cualquier razón, necesitan tener una fuerte correlación entre su código y el funcionamiento de la máquina subyacente. Ya sea su eficiencia, o controladores de dispositivos de programación, o interacción con los servicios de SO de nivel inferior, o lo que sea, C++ se adapta mejor a esas tareas.

C#, Java, Objective-C requieren un sistema de tiempo de ejecución mucho más grande y rico para soportar su ejecución. Ese tiempo de ejecución tiene que ser entregado al sistema en cuestión - preinstalado para apoyar el funcionamiento de su software. Y esa capa tiene que mantenerse para varios sistemas de destino, personalizada por ALGÚN OTRO LENGUAJE para que funcione en esa plataforma. Y esa capa intermedia, esa capa adaptativa entre el sistema operativo host y el código, el tiempo de ejecución, es casi siempre escrito en un lenguaje como C o C++ donde la eficiencia es #1, donde la comprensión predecible de la interacción exacta entre software y hardware puede ser bien entendida y manipulada para obtener la máxima ganancia.

Me encanta Smalltalk, Objective-C, y tener un sistema de tiempo de ejecución rico con reflexión, metadatos, recolección de basura, etc. Increíble código se puede escribir para tomar ventaja de estas instalaciones! Pero eso es simplemente una capa más alta en la pila, una capa que debe descansar en capas más bajas, que en última instancia deben sentarse en el sistema operativo y el hardware. Y siempre necesitaremos un lenguaje que sea el más adecuado para construir esa capa: C++ / C / Assembler.

Anexo: C++11/14 continúan expandiendo la capacidad de C++ para soportar abstracciones y sistemas de alto nivel. El enhebrado, la sincronización, los modelos de memoria precisos y las definiciones de máquinas abstractas más precisas permiten a los desarrolladores de C++ lograr muchas de las abstracciones de alto nivel que algunos de estos lenguajes de alto nivel solía tener dominio exclusivo, mientras continuaba proporcionando un rendimiento cercano al metal y una excelente previsibilidad (es decir, subsistemas de tiempo de ejecución mínimo). Tal vez las instalaciones de reflexión se habilitarán selectivamente en una futura revisión de C++, para aquellos que lo deseen - o tal vez una biblioteca proporcionará tales servicios de tiempo de ejecución (tal vez hay uno ahora, o los comienzos de uno en boost?).

 14
Author: Mordachai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-14 18:20:27

Si realmente desea entender las decisiones de diseño que rodean a C++, busque una copia del The Annotated C++ Reference Manual de Ellis y Stroustrup. NO está actualizado con el último estándar, pero pasa por el estándar original y explica cómo funcionan las cosas y, a menudo, cómo se volvieron de esa manera.

 11
Author: Michael Kohne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 14:57:57

La reflexión puede ser y ha sido implementada en c++ antes.

No es una característica nativa de c++ porque tiene un alto costo (memoria y velocidad) que no debería ser establecido por defecto por el lenguaje - el lenguaje está orientado al "máximo rendimiento por defecto".

Como usted no debe pagar por lo que no necesita, y como usted mismo dice que se necesita más en los editores que en otras aplicaciones, entonces debe implementarse solo donde lo necesita, y no "forzado" a todos los código (no necesitas reflexionar sobre todos los datos con los que trabajarás en un editor u otra aplicación similar).

 8
Author: Klaim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 12:58:30

La reflexión para los lenguajes que la tienen se trata de la cantidad de código fuente que el compilador está dispuesto a dejar en su código objeto para habilitar la reflexión, y la cantidad de maquinaria de análisis disponible para interpretar esa información reflejada. A menos que el compilador mantenga todo el código fuente, la reflexión se verá limitada en su capacidad para analizar los hechos disponibles sobre el código fuente.

El compilador de C++ no guarda nada (bueno, ignorando RTTI), por lo que no obtienes reflexión {[3] {} en[4]} el lenguaje. (Los compiladores Java y C# solo mantienen los nombres de clases, métodos y tipos de retorno, por lo que obtiene un poco de datos de reflexión, pero no puede inspeccionar expresiones o estructura del programa, y eso significa que incluso en esos lenguajes "habilitados para la reflexión" la información que puede obtener es bastante escasa y, en consecuencia, realmente no puede hacer mucho análisis).

Pero puede pasar fuera de el lenguaje y obtener capacidades de reflexión completas. La respuesta a otro la discusión de desbordamiento de pila en reflexión en C discute esto.

 8
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:48

La razón por la que C++ no tiene reflexión es que esto requeriría que los compiladores agregaran información de símbolos a los archivos objeto, como qué miembros tiene un tipo de clase, información sobre los miembros, sobre las funciones y todo. Esto esencialmente haría que los archivos include fueran inútiles, ya que la información enviada por las declaraciones se leería de esos archivos objeto (módulos entonces). En C++, una definición de tipo puede ocurrir varias veces en un programa mediante la inclusión de las cabeceras respectivas (siempre que todas esas definiciones son las mismas), por lo que habría que decidir dónde poner la información sobre ese tipo, así como nombrar una complicación aquí. La agresiva optimización realizada por un compilador de C++, que puede optimizar docenas de instanciaciones de plantillas de clase, es otro punto fuerte. Es posible, pero como C++ es compatible con C, esto se convertiría en una combinación incómoda.

 6
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 13:02:14

Hay toneladas de casos para usar la reflexión en C++ que no se pueden abordar adecuadamente usando construcciones de tiempo de compilación como la metaprogramación de plantillas.

N3340 propone punteros enriquecidos como una forma de introducir la reflexión en C++. Entre otras cosas, aborda el problema de no pagar por una función a menos que la use.

 3
Author: pong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-14 19:04:58

Según Alistair Cockburn, el subtipo no se puede garantizar en un entorno reflectante.

La reflexión es más relevante para los sistemas de tipificación latente. En C++, sabes qué tipo tienes y sabes qué puedes hacer con él.

 2
Author: Nilone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-06 16:56:12

La reflexión podría ser opcional, como una directiva de preprocesador. Algo así como

#pragma enable reflection

De esa manera podemos tener lo mejor de ambos mundos, sin estas bibliotecas pragma se crearían sin reflexión (sin gastos generales como se discutió), entonces sería hasta el desarrollador individual si quieren velocidad o facilidad de uso.

 2
Author: user1401491,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-25 13:00:31

Si C++ pudiera tener:

  • datos de miembros de clase para nombres de variables, tipos de variables y el modificador const
  • un iterador de argumentos de función (solo posición en lugar de nombre)
  • datos de miembros de clase para nombres de funciones, tipo de retorno y el modificador const
  • lista de clases principales (en el mismo orden definido)
  • datos para los miembros de la plantilla y las clases principales; la plantilla expandida (lo que significa que el tipo real estaría disponible para la API de reflexión y no "información de plantilla sobre cómo llegar")

Eso sería suficiente para crear bibliotecas muy fáciles de usar en el quid del procesamiento de datos sin tipo que es tan frecuente en las aplicaciones web y de bases de datos de hoy en día (todos los or, mecanismos de mensajería, analizadores xml/json, serialización de datos, etc.).

Por ejemplo, la información básica soportada por la macro Q_PROPERTY (parte de Qt Framework) http://qt.nokia.com/doc/4.5/properties.html ampliado para cubrir los métodos de clase y e) - sería extraordinariamente beneficioso para C++ y para la comunidad de software en general.

Ciertamente, la reflexión a la que me refiero no cubriría el significado semántico o cuestiones más complejas (como los números de línea del código fuente de los comentarios, el análisis del flujo de datos, etc.), pero tampoco creo que sean necesarias para formar parte de un estándar de lenguaje.

 2
Author: Lightness Races in Orbit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-13 14:06:58

Algunos buenos enlaces sobre la reflexión en C++ que acabo de encontrar:

Working Paper of C++ Standard: Aspects of Reflection in C++

Un ejemplo sencillo de reflexión usando plantillas

 1
Author: amit_grepclub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 14:49:22

La reflexión en C++ , creo que es de vital importancia si C++ se va a utilizar como un lenguaje para el Acceso a Bases de Datos, manejo de sesiones Web/http y desarrollo de GUI. La falta de reflexión evitaMs (como Hibernate o LINQ), analizadores XML y JSON que instancian clases, serialización de datos y muchos otros thigns (donde inicialmente se deben usar datos sin tipo para crear una instancia de una clase).

Un conmutador de tiempo de compilación disponible para un desarrollador de software durante el proceso de compilación puede ser utilizar para eliminar esta preocupación de 'usted paga por lo que usa'.

I un firmwaredeveloper no necesita la reflexión para leer datos de un puerto serie then entonces bien no utilice el interruptor. Pero como desarrollador de bases de datos que quiere seguir usando C++, estoy constantemente en fase con un código horrible y difícil de mantener que mapea Datos entre miembros de datos y construcciones de bases de datos.

Ni la serialización de Boost ni ningún otro mecanismo están realmente resolviendo la reflexión must compilador {y una vez hecho, C++ se volverá a enseñar en las escuelas y se utilizará en el software que se ocupa del procesamiento de datos

Para mí esta edición #1 (y naitive threading primitives es la edición #2).

 1
Author: vsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-02 21:04:48

Es básicamente porque es un "extra " opcional". Muchas personas eligen C++ sobre lenguajes como Java y C # para tener más control sobre la salida del compilador, por ejemplo, un programa más pequeño y/o más rápido.

Si elige agregar reflexión, hay varias soluciones disponibles.

 0
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:02