Una pregunta común, aquí y en otros lugares. ¿C++ es adecuado para sistemas embebidos?
¿Microcontroladores? ¿RTOS? ¿Tostadoras? PC integradas?
¿Es OOP útil en microcontroladores?
¿C++ aleja demasiado al programador del hardware para ser eficiente?
¿Debería considerarse el C++ de Arduino (sin administración de memoria dinámica, plantillas, excepciones) como "C++ real"?
(Con suerte, esta wiki servirá como un lugar para contener esta potencial guerra santa)
Sí, C++ sigue siendo útil en sistemas integrados. Como todos los demás han dicho, todavía depende del sistema en sí, como un uC de 8 bits probablemente sería un no-no en mi libro a pesar de que hay un compilador y algunas personas lo hacen (estremecimiento). Todavía hay una ventaja en el uso de C ++, incluso cuando lo reduce a algo como "C +", incluso en un micromundo de 8 bits. ¿Qué quiero decir con "C+"? Me refiero a no usar new/delete, evitar excepciones, evitar clases virtuales con herencia, posiblemente evitar la herencia por completo, tener mucho cuidado con las plantillas, usar funciones en línea en lugar de macros y usar const
variables en lugar de #defines
.
He estado trabajando tanto en C como en C++ en sistemas integrados durante más de una década, y parte de mi entusiasmo juvenil por C++ definitivamente se ha desvanecido debido a algunos problemas del mundo real que sacuden la ingenuidad de uno. He visto lo peor de C ++ en sistemas integrados a los que me gustaría referirme como "programadores de CS enloquecidos en un mundo EE". De hecho, eso es algo en lo que estoy trabajando con mi cliente para mejorar este código base que tienen entre otros.
El peligro de C++ se debe a que es una herramienta muy, muy poderosa, como una espada de dos filos que puede cortarle un brazo y una pierna si no se educa y se disciplina adecuadamente en su lenguaje y en la programación general. C es más como una espada de un solo filo, pero igual de afilada. Con C++ es demasiado fácil obtener niveles muy altos de abstracción y crear interfaces ofuscadas que pierden sentido a largo plazo, y eso se debe en parte a la flexibilidad de C++ para resolver el mismo problema con muchas funciones de lenguaje diferentes (plantillas, programación orientada a objetos, procedimientos, RTTI, OOP+plantillas, sobrecarga, en línea).
Terminé dos seminarios de 4 horas sobre software integrado en C++ impartidos por el gurú de C++, Scott Meyers. Señaló algunas cosas sobre las plantillas que nunca antes había considerado y cuánto más pueden ayudar a crear código crítico para la seguridad. El quid de la cuestión es que no se puede tener código inactivo en un software que tiene que cumplir estrictos requisitos de código críticos para la seguridad. Las plantillas pueden ayudarlo a lograr esto, ya que el compilador solo crea el código que necesita al crear instancias de plantillas. Sin embargo, uno debe educarse más a fondo en su uso para diseñar correctamente esta función, que es más difícil de lograr en C porque los enlazadores no siempre optimizan el código muerto.
Scott Meyers es un gran defensor de las plantillas y el uso sensato de la inserción, y debo decir que todavía soy escéptico acerca de las plantillas. Tiendo a alejarme de ellos, aunque él dice que solo deben aplicarse donde se conviertan en la mejor herramienta. También destaca que C++ le brinda las herramientas para crear interfaces realmente buenas que son fáciles de usar correctamente y dificultan su uso incorrecto. Una vez más, esa es la parte difícil. Uno debe llegar a un nivel de dominio en C++ antes de poder saber cómo aplicar estas características de la manera más eficiente para ser la mejor solución de diseño.
Lo mismo ocurre con la programación orientada a objetos. En el mundo integrado, debe familiarizarse con el tipo de código que arrojará el compilador para saber si puede manejar los costos de tiempo de ejecución del polimorfismo en tiempo de ejecución. También debe estar dispuesto a realizar mediciones para demostrar que su diseño cumplirá con los requisitos de su fecha límite. ¿Esa nueva clase InterruptManager hará que mi latencia de interrupción sea demasiado larga? Hay otras formas de polimorfismo que pueden adaptarse mejor a su problema, como el polimorfismo en tiempo de enlace, que C también puede hacer, pero C ++ puede hacerlo a través del patrón de diseño Pimpl (puntero opaco) .
Digo todo eso para decir que C++ tiene su lugar en el mundo integrado. Puedes odiarlo todo lo que quieras, pero no va a desaparecer. Se puede escribir de una manera muy eficiente, pero es más difícil aprender cómo hacerlo correctamente que con C. A veces puede funcionar mejor que C para resolver un problema y, a veces, expresar una mejor interfaz, pero nuevamente, tienes que infórmate y no tengas miedo de aprender cómo hacerlo.
On the note of Computer Science majors going crazy in EE land ...
--> Érase una vez algunos consultores profesionales de CS con nosotros que tenían muchos años de experiencia y estaban convencidos de que programar sin excepciones es simplemente nauseabundo. Habilitaron excepciones con -fexceptions
los controladores integrados (lo cual fue desaconsejado activamente por el fabricante de esos controladores), mientras se quejaban de que la gente de EE simplemente no entendía el software y lanzaba excepciones de cualquier manera. (-‸ლ) Después de un par de años en el infierno de desarrollo, el proyecto murió.C++ es absolutamente adecuado para sistemas integrados. Ahora uso la presencia/ausencia de buenas herramientas de desarrollo (o la falta de ellas) como mi criterio principal para usar o no un microprocesador en particular.
Áreas de C ++ que son buenas para usar en sistemas integrados porque tienen bajos costos de recursos:
Áreas aceptables:
Áreas que no se deben usar, principalmente debido a la sobrecarga de tiempo de ejecución que es inaceptable en sistemas pequeños:
foo
llama bar
dentro de un bloque try
/ y crea algunos objetos y llama , lo que genera una excepción, el sistema tiene que llamar de alguna manera a los destructores de los objetos creados antes de devolver el control a . A menos que las excepciones estén completamente deshabilitadas, no tendrá forma de saber si podría arrojar alguna y, por lo tanto, debe incluir código adicional para permitir esa posibilidad. Me gustaría ver una variación de C++ con "excepciones marcadas" para lidiar con eso; si las rutinas que podrían permitir el escape de excepciones...
catch
bar
boz
bar
foo
bar
boz
add r15,r14,#2
de en lugar de mov r15,r14
; para salir por excepción, ldrhs r0,[r14] / add r15,r14,r0
. Costo de ciclo cero para la salida normal y sin restricciones de marco de pila.throw
código pueda encontrar el catch
, ya sea requiriendo que el código C o ensamblador siga un estricto protocolo de marco de pila, o bien utilizando una ubicación estática para contener la información necesaria o un puntero a ella.foo
llama a la rutina de lenguaje ensamblador bar
, que coloca elementos arbitrarios desconocidos en la pila antes de llamar a la rutina boz
de C++ y boz
lanza una excepción, el sistema de alguna manera tiene que encontrar foo
el marco de la pila. No hay forma de que eso suceda sin modificar la rutina del lenguaje ensamblador bar
para que su valor inicial de puntero de pila esté disponible para el código que está llamando, o modificar el multitarea para guardar/restaurar un puntero de marco de excepción estático.Sí, C++ ciertamente es adecuado para sistemas integrados. Primero aclaremos un par de conceptos erróneos sobre la diferencia entre C y C++:
En un micro incrustado, siempre necesitará usar lenguajes de alto nivel con cuidado si le preocupan las limitaciones de tiempo o espacio. Por ejemplo, muchas MCU no manejan bien los punteros y, por lo tanto, son muy ineficientes cuando usan la pila. Esto significa que debe tener cuidado al pasar variables a funciones, usar matrices y punteros y recursividad. Una simple línea de C como:
a[i] = b[j] * c[k];
puede generar alrededor de 4 páginas de instrucciones dependiendo de la naturaleza de esas variables.
Cada vez que utiliza un lenguaje de alto nivel y le preocupan las limitaciones de tiempo y espacio, necesita saber cómo cada función de ese lenguaje se traduce en instrucciones de máquina en su MCU (al menos, cada función que usa). Esto es cierto para C, C++, Ada, lo que sea. Probablemente todos los idiomas contengan funciones que no se traducen de manera eficiente en MCU pequeños. Siempre verifique las listas de desensamblaje para asegurarse de que el compilador no esté generando montones de instrucciones para algo trivial.
¿C es adecuado para MCU integrados? Sí, siempre y cuando vigiles el código generado.
¿C++ es adecuado para MCU integrados? Sí, siempre y cuando vigiles el código generado.
He aquí por qué creo que C++ es mejor que C incluso en MCU de 8 bits: C++ brinda soporte mejorado para:
Ninguna de estas características es más pesada que las características típicas de C.
A medida que avanza a MCU de 16 o 32 bits, entonces comienza a tener sentido usar características más pesadas de C (pila, montón, punteros, matrices, printf, etc.) De la misma manera, en una MCU más potente se vuelve apropiado para usar características más pesadas de C++ (pila, montón, referencias, STL, nuevo/eliminar).
Por lo tanto, no hay necesidad de estremecerse al pensar en C++ en un PIC16. Si conoce su idioma y su MCU correctamente, entonces sabrá cómo usarlos juntos de manera efectiva.
a[i] = b[j] * c[k];
puede generar alrededor de 4 páginas de instrucciones dependiendo de la naturaleza de esas variables". Si su MCU/compilador hace esto, es porque está usando una CPU de aficionado de garaje de los años 80.Siempre encuentro estos debates entretenidos de leer. No tanto por la discusión intelectual sobre los pros y los contras de los distintos idiomas disponibles, sino porque normalmente puedes fijar la postura de alguien sobre el tema en función de su trabajo/experiencia/área de interés. Está a la altura de los argumentos de "optimización prematura" donde los programadores de mantenimiento y especialistas en informática citan a Knuth de izquierda a derecha y aquellos que trabajan en el mundo real donde el rendimiento importa piensan que están todos locos (soy miembro del último grupo para ser justo).
Al final del día, puede desarrollar un software excelente en C o C++ o insertar lenguaje aquí . Todo se reduce a las capacidades del desarrollador, no al lenguaje. Por lo general, solo se requiere ser un experto en un idioma si ha elegido el idioma incorrecto para empezar y ahora necesita modificarlo para resolver su problema; en la mayoría de los casos, estas son las únicas situaciones en las que necesita sumergirse en características oscuras o compilador. Trucos para lograr el objetivo.
A menudo escucho a la gente comenzar estos argumentos como "Soy un experto en lenguaje X y bla, bla". Sinceramente, desacredito inmediatamente a estas personas porque, en mi opinión, ya han abordado el problema desde el ángulo equivocado y todo lo que sigue está viciado. por su deseo de usar su herramienta para resolver el problema y mostrar lo "genial" que es.
Muy a menudo observo a los desarrolladores elegir primero un conjunto de herramientas e intentar adaptarlo a su problema en segundo lugar, lo que es completamente incorrecto y da como resultado soluciones de mierda.
Como mencioné en un comentario a otra respuesta, estas guerras de idiomas a menudo se convierten en argumentos de que el lenguaje X le permite al programador hacer cosas más tontas. Si bien es entretenido leer, todas estas declaraciones realmente significan que tiene un problema para contratar buenos desarrolladores y necesita abordar ese problema directamente en lugar de tratar de curar la situación al continuar contratando a malos desarrolladores y eligiendo herramientas para que puedan hacer lo menos posible. daño como sea posible.
En mi opinión, los buenos desarrolladores, ya sea desarrollo de software o hardware, investigan el problema, diseñan una solución y encuentran las herramientas que les permiten expresar la solución de la 'mejor manera'. No debería importar si la herramienta requerida es algo que nunca ha usado antes, después de haber usado 3 o 4 idiomas/herramientas de desarrollo para proyectos, seleccionar una nueva debería tener un impacto mínimo en su tiempo de desarrollo.
Por supuesto, 'la mejor manera' es un término subjetivo y también debe definirse en la fase de investigación. Uno debe considerar una multitud de cuestiones: rendimiento, facilidad de expresión, densidad de código, etc. en función del problema en cuestión. No incluí la mantenibilidad en esa lista por una razón, no me importa qué idioma elija, si eligió la herramienta adecuada y se tomó el tiempo para comprender el problema, esto debería ser 'gratis'. El código difícil de mantener es a menudo el resultado de elegir la herramienta incorrecta o una estructura de sistema deficiente, lo que resulta en un feo desorden para que funcione.
Afirmar que cualquier idioma es 'mejor' que cualquier otro es una tontería sin definir un problema de interés particular. Un enfoque orientado a objetos no siempre es mejor que un enfoque funcional. Hay algunos problemas que se prestan muy bien a un paradigma de diseño orientado a objetos. Hay muchos que no. Se puede hacer la misma afirmación acerca de muchas características del lenguaje sobre las que la gente parece disfrutar insistiendo.
Si dedica más del 20% de su tiempo a un problema escribiendo código, probablemente esté produciendo un sistema muy deficiente, o tenga desarrolladores muy deficientes (o todavía esté aprendiendo). Debería pasar la mayor parte de su tiempo al principio diagramando el problema y determinando cómo interactúan varias piezas de la aplicación. Poner a un grupo de desarrolladores talentosos en una sala con un marcador y un problema para resolver y decirles que no pueden escribir ningún código o elegir ninguna herramienta hasta que se sientan cómodos con todo el sistema contribuirá más a mejorar la calidad del rendimiento y velocidad de desarrollo que elegir cualquier herramienta nueva y novedosa garantizada para mejorar el tiempo de desarrollo. (busque el desarrollo de scrum como referencia para el polo opuesto a mi argumento)
A menudo, la desafortunada realidad es que muchas empresas solo pueden medir el valor de un desarrollador por la cantidad de líneas escritas o por ver 'resultados tangibles'. Ven las 3 semanas en una habitación con un marcador como una pérdida de productividad. Los desarrolladores a menudo se ven obligados a acelerar la etapa de "pensamiento" del desarrollo o se ven obligados a usar un conjunto de herramientas por algún problema político dentro de la empresa, "El hermano de mi jefe trabaja para IBM, así que solo podemos usar sus herramientas", ese tipo de basura. . O peor aún, obtiene un conjunto de requisitos en constante cambio de la empresa porque no son capaces de realizar una investigación de mercado adecuada o no comprenden el impacto de los cambios en el ciclo de desarrollo.
Perdón por estar un poco fuera de tema con esta diatriba, tengo opiniones bastante fuertes sobre este tema.
En mi experiencia, C ++ generalmente no se adapta a los pequeños sistemas integrados. Con lo que quiero decir, microcontroladores y dispositivos sin sistema operativo.
Muchas técnicas de programación orientada a objetos de C++ se basan en la asignación de memoria dinámica. Esto a menudo falta en los sistemas pequeños.
STL y Boost realmente demuestran el poder de C++, ambos ocupan un espacio enorme.
C++ anima al programador a abstraerse de la máquina, donde en sistemas restringidos tiene que ser adoptada.
El año pasado, transfirí un producto comercial de escritorio remoto a teléfonos móviles. Fue escrito en C++ y se ejecutó en Windows, Linux y OSX. Pero dependía en gran medida de STL, memoria dinámica y excepciones de C++. Para ponerlo en marcha en entornos WinCE, Symbian y sin sistema operativo, una reescritura de C era la opción más sensata.
Cualquier lenguaje puede ser adecuado para un sistema embebido. Embebido solo significa: parte de un aparato más grande, a diferencia de una computadora de uso gratuito.
La pregunta tiene más relevancia cuando se pregunta por un sistema de recursos limitados o en tiempo real (duro) .
Para un sistema en tiempo real, C ++ es uno de los lenguajes más avanzados que aún es apropiado cuando se programa para restricciones de tiempo estrictas. Con la excepción del uso del montón (operador libre), no tiene construcciones que tengan un tiempo de ejecución indeterminado, por lo que puede probar si su programa cumple con los requisitos de tiempo y, con un poco más de experiencia, incluso podría predecirlo. Por supuesto, se debe evitar el uso del almacenamiento dinámico, aunque el nuevo operador aún se puede usar para una asignación única. Las construcciones que C++ ofrece sobre C se pueden utilizar en un sistema integrado: OO, excepciones, plantillas.
Para sistemas con recursos muy limitados (chips de 8 bits, menos de unos pocos Kb de RAM, pila no accesible), C++ completo podría no ser adecuado, aunque aún podría usarse como un 'mejor C'.
Creo que es desafortunado que Ada parezca usarse solo en algunos nichos. En muchos sentidos, es un Pascal ++, pero sin la carga de ser compatible hacia arriba con un lenguaje que ya era un desastre serio para empezar. (editar: el lío serio es, por supuesto, C. Pascal es un lenguaje hermoso pero algo poco práctico).
================================================== ==============
EDITAR: estaba escribiendo una respuesta a una nueva pregunta ("¿En qué casos es necesario C ++ cuando estamos programando microcontroladores"?) Que se cerró refiriéndose a esta, así que agregaré lo que escribí:
Nunca hay una razón absoluta para el uso de cualquier lenguaje de programación, pero puede haber argumentos que tengan más o menos peso en una situación particular. Se pueden encontrar discusiones sobre esto en muchos lugares, con posiciones tomadas que van desde "nunca usar C++ para un microcontrolador" hasta "siempre usar C++". Estoy más con la última posición. Puedo dar algunos argumentos, pero tendrás que decidir por ti mismo cuánto peso tienen en una situación particular (y en qué dirección).
Mi blog tiene algunos escritos sobre el uso de C++ en sistemas pequeños (= microcontroladores).
Espero agregar más luz que calor a esta discusión sobre C++ en sistemas bare metal y con recursos limitados.
Problemas en C++:
Las excepciones son especialmente un problema de RAM, ya que el "búfer de emergencia" requerido (donde va la excepción de falta de memoria, por ejemplo) puede ser más grande que la RAM disponible y ciertamente es un desperdicio para los microcontroladores. Para obtener más información, consulte n4049 y n4234 . Deben estar apagados (que actualmente es un comportamiento no especificado, así que asegúrese de no tirarlos nunca). SG14 está trabajando actualmente en mejores formas de hacer esto.
RTTI probablemente nunca valga la pena, debe desactivarse
Construcciones de depuración grandes, aunque esto no es un problema en el desarrollo de escritorio clásico, si la depuración no cabe en el chip, puede ser un problema. El problema surge del código con plantilla o de las llamadas a funciones adicionales agregadas para mayor claridad. El optimizador eliminará nuevamente estas llamadas de funciones adicionales y la claridad o flexibilidad añadidas pueden ser una gran ventaja, sin embargo, en las compilaciones de depuración esto puede ser un problema.
Asignación de montones. Aunque STL permite el uso de asignadores personalizados, esto puede ser complejo para la mayoría de los programadores. La asignación del almacenamiento dinámico no es determinista (es decir, no es un tiempo real estricto) y la fragmentación puede dar lugar a situaciones inesperadas de falta de memoria a pesar de haber trabajado en las pruebas. La contabilidad que necesita el montón para realizar un seguimiento del espacio libre y el tamaño variable puede ser un problema con los objetos pequeños. Por lo general, es mejor usar la asignación de grupos (tanto en C como en C ++), pero esto puede ser anormal para los programadores de C ++ acostumbrados a usar solo el montón.
El polimorfismo en tiempo de ejecución y otras llamadas indirectas suelen ser un gran impacto en el rendimiento, el problema suele ser más porque el optimizador no puede ver a través de ellos más que la búsqueda real y el salto a la dirección. Las llamadas indirectas deben evitarse por este motivo en C y C++, mientras que en C++ están más arraigadas en la cultura (y son bastante útiles en otros dominios).
la interfaz implícita con clib puede ser problemática. Puede ser contrario a la intuición que los problemas de clib estén en la categoría de C++, pero el problema surge del intercambio implícito de recursos en entornos concurrentes (el intercambio es más explícito en C). El uso de la implementación común de newLib a menudo arrastra una gran cantidad de información que generalmente no se necesita en uC, por otro lado, newLibNanno no es reentrante, por lo que el acceso debe serializarse (simplificando demasiado aquí). Este es un problema para C también, pero el acceso es más explícito. Como regla general, uno no debería usar nada del espacio de nombres estándar en el contexto de ISR a menos que esté seguro de que no accede al estado en clib de alguna manera (errorno o el montón, por ejemplo). También es importante si está utilizando subprocesos (prefiero RTC) para anular nuevos y eliminar para sincronizar el acceso a malloc y gratis.
En conclusión, C ++ tiene algunos problemas, pero todos ellos son esencialmente reparables o evitables.
Ahora para C, aquí el problema es de orden superior. No tengo la capacidad sintáctica en C para abstraer cosas de una manera que pueda realizar la optimización o verificar invariantes en tiempo de compilación. Por lo tanto, no puedo encapsular correctamente las cosas de manera que el usuario no necesite saber cómo funcionan para poder usarlas y la mayor parte de mi detección de errores se realiza en tiempo de ejecución (que no solo es demasiado tarde sino que también agrega costos). Esencialmente, la única forma de ser genérico en C es a través de datos, paso una cadena de formato a printf o scanf que se evalúa en tiempo de ejecución, por ejemplo. Entonces es bastante difícil para el compilador demostrar que no estoy usando algunas de las opciones que son teóricamente posibles cuando se pasan los datos correctos, lo que significa una posible generación de código muerto y una pérdida del potencial de optimización.
Sé que puedo estar desatando una tormenta de mierda aquí, pero mi experiencia con los microcontroladores de 32 bits es que en una comparación de manzanas con manzanas de C y C ++, ambos escritos por expertos (como en C ++ potencialmente altamente plantillado) C ++ es el lenguaje mucho más eficiente tan pronto como cualquier cosa debe ser genérica (como en cualquier biblioteca) y son esencialmente equivalentes en casos no genéricos. También es más fácil para un novato aprovechar la experiencia de un implementador de bibliotecas experto en C++.
Al mismo tiempo, en realidad hay pocas funciones a las que no puedo pasar datos incorrectos, tan pronto como la entrada no sea un int sino un something
para el cual estoy usando un int como método de representación, entonces existe la posibilidad de obtenerlo. incorrecto (pasar un valor inválido o una 'otra Cosa' en lugar de un 'algo'). En C, mi único método para verificar si el usuario se equivocó es en tiempo de ejecución. En C++ tengo la capacidad de realizar algunas comprobaciones, no todas las comprobaciones pero algunas comprobaciones en tiempo de compilación que son gratuitas.
Al final del día, un equipo C suele ser tan poderoso como su programador más débil y el beneficio del código resultante tiene un multijugador de 1 o una penalización de rendimiento. Lo que quiero decir con esto es que es de alto rendimiento para uno y solo un trabajo único en un entorno único de decisiones de diseño únicas o es lo suficientemente genérico como para usarse en múltiples entornos (otro microcontrolador, otra estrategia de administración de memoria, otra latencia vs. compensaciones de rendimiento, etc., etc.) pero tiene un costo de rendimiento inherente.
En C++, los expertos pueden encapsular las cosas y usarlas en muchos entornos donde la generación de código de tiempo de compilación se adapta a la tarea específica y la verificación estática evita que los usuarios hagan cosas estúpidas sin costo alguno. Aquí tenemos mucho menos compromiso entre ser genérico y ser rápido y, por lo tanto, en última instancia, desde el punto de vista de costo versus beneficio, son el lenguaje más eficaz, seguro y productivo.
Es una crítica válida que todavía hay una gran escasez de buenas bibliotecas de C ++ para embebido, esto puede conducir a decisiones pragmáticas para usar principalmente C en un compilador de C ++. Las decisiones de usar solo C en un proyecto son esencialmente ideológicas, por la necesidad de soporte heredado o por admitir que el equipo no es lo suficientemente disciplinado para abstenerse de un conjunto muy selecto de cosas estúpidas que uno puede hacer en C++ pero no en C. y, al mismo tiempo, lo suficientemente disciplinado como para no hacer ninguna de las muchas cosas estúpidas de las que uno no puede protegerse en C pero sí en C++.
Mi experiencia: acaba de salir de la escuela de formación con antiguos programadores de Bell Labs; estado trabajando durante 3 años, 2 en proyecto de investigación de pregrado; adquisición de datos / control de procesos en VB.NET. Pasé 1,5 años trabajando en una aplicación de base de datos empresarial en VB6. Actualmente trabajando en proyecto para PC embebido con 2GB de almacenamiento, 512MB de RAM, CPU 500MHz x86; varias aplicaciones que se ejecutan simultáneamente escritas en C++ con un mecanismo IPC en el medio. Sí, soy joven.
Mi opinión: creo que C ++ puede funcionar de manera efectiva dado el entorno que he escrito anteriormente . Es cierto que el rendimiento en tiempo real no es un requisito para la aplicación en la que estoy, y en algunas aplicaciones integradas, eso puede ser un problema. Pero aquí están las cosas que he aprendido:
C++ es fundamentalmente diferente de C (es decir, no hay C/C++). Si bien todo lo que es C válido es C++ válido, C++ es un lenguaje muy diferente y uno necesita aprender a programar en C++, no en C, para usarlo de manera efectiva en cualquier situación. En C++, necesita programar orientado a objetos, no procedimentalmente, y no un híbrido de los dos (clases grandes con muchas funciones). En general, debe centrarse en crear clases pequeñas con pocas funciones y componer todas las clases pequeñas juntas en una solución más grande. Uno de mis compañeros de trabajo me explicó que solía programar procedimentalmente en objetos, lo cual es un gran lío y es difícil de mantener. Cuando comencé a aplicar más técnicas orientadas a objetos, descubrí que la mantenibilidad/legibilidad de mi código aumentó.
C++ proporciona funciones adicionales en forma de desarrollo orientado a objetos que pueden proporcionar una manera de simplificar el código para que sea más fácil de leer y mantener . Honestamente, no creo que haya mucho en el camino de una mejora en la eficiencia de rendimiento/espacio al hacer OOP. Pero creo que la programación orientada a objetos es una técnica que puede ayudar a dividir un problema complejo en muchas piezas pequeñas. Y eso es útil para las personas que trabajan en el código, un elemento de este proceso que no debe ignorarse.
Muchos argumentos en contra de C++ tienen que ver principalmente con la asignación de memoria dinámica. C tiene este mismo problema también. Puede escribir una aplicación orientada a objetos sin usar memoria dinámica, aunque uno de los beneficios de usar objetos es que puede asignar estas cosas dinámicamente de manera sencilla. Al igual que en C, debe tener cuidado con la forma en que administra los datos para reducir las fugas de memoria, pero la técnica RAII lo simplifica en C++ (hace que la memoria dinámica se destruya automáticamente encapsulándola en objetos). En algunas aplicaciones, donde cada ubicación de memoria cuenta, esto puede ser demasiado salvaje y confuso para administrar.
EDITAR:
Sí, el problema con C++ es la mayor huella del código.
En algunos sistemas, está contando bytes y, en ese caso, tendrá que aceptar un costo de ejecución que, cerca de los límites de sus sistemas, aumenta el costo de desarrollo de C.
Pero, incluso en C, para un sistema bien diseñado necesita mantener todo encapsulado. Los sistemas bien diseñados son difíciles, y C++ brinda a los programadores un lugar para un método de desarrollo muy estructurado y controlado. Aprender programación orientada a objetos tiene un costo, y si desea cambiar a él, lo acepta y, en muchos casos, la gerencia preferiría continuar con C y no pagar el costo, ya que es difícil medir los resultados de un cambio que aumenta la productividad. Puede ver un artículo del gurú de los sistemas integrados Jack Ganssle aquí .
La gestión de memoria dinámica es el diablo. Realmente no, el diablo es el enrutamiento automático, la gestión dinámica de la memoria funciona muy bien en una PC, pero puede esperar reiniciar una PC al menos cada pocas semanas. Descubrirá que, a medida que un sistema integrado continúa funcionando durante 5 años, la administración dinámica de la memoria realmente puede estropearse y comenzar a fallar. Ganssle analiza cosas como apilar y amontonar en su artículo.
Hay algunas cosas en C++ que son más propensas a causar problemas y usan muchos recursos, la eliminación de la administración de memoria dinámica y las plantillas son grandes pasos para mantener la huella de C++ más cerca de la huella de C. Esto sigue siendo C++, no necesita dinámico gestión de memoria o plantillas para escribir bien en C++. No me di cuenta de que eliminaron las excepciones, considero que las excepciones son una parte importante de mi código que elimino en el lanzamiento, pero uso hasta ese momento. En las pruebas de campo, puedo hacer que las excepciones generen mensajes para informarme sobre la detección de una excepción.
Pensé que esta perorata anti-C++ de Linus Torvalds era interesante.
Una de las peores características absolutas de C++ es cómo hace que muchas cosas dependan del contexto, lo que significa que cuando observa el código, una vista local rara vez brinda suficiente contexto para saber qué está sucediendo.
No está hablando del mundo de los sistemas integrados, sino del desarrollo del kernel de Linux. Para mí, la relevancia proviene de esto: C ++ requiere comprender un contexto más amplio y puedo aprender a usar un conjunto de plantillas de objetos, no confío en mí mismo para recordarlos cuando tenga que actualizar el código en unos meses.
(Por otro lado, actualmente estoy trabajando en un dispositivo integrado que usa Python (no C ++, pero usa el mismo paradigma OOP) que tendrá exactamente ese problema. En mi defensa, es un sistema integrado lo suficientemente poderoso como para llamarlo PC Hace 10 años.)
Creo que otras respuestas defendieron bastante bien los pros y los contras y los factores de decisión, por lo que me gustaría resumir y agregar algunos comentarios.
Para microcontroladores pequeños (8 bits), de ninguna manera. Solo estás pidiendo hacerte daño, no hay ganancia y renunciarás a demasiados recursos.
Para los microcontroladores de gama alta (por ejemplo, 32 bits, 10 o 100 MB para RAM y almacenamiento) que tienen un sistema operativo decente, está perfectamente bien y, me atrevería a decir, incluso recomendado.
Entonces la pregunta es: ¿dónde está el límite?
No estoy seguro, pero una vez desarrollé un sistema para un uC de 16 bits con 1 MB de RAM y 1 MB de almacenamiento en C++, solo para arrepentirme más tarde. Sí, funcionó, pero el trabajo extra que tuve no valió la pena. Tenía que hacer que encajara, asegurarme de que cosas como las excepciones no produjeran fugas (el soporte de OS+RTL tenía bastantes errores y era poco fiable). Además, una aplicación orientada a objetos normalmente hace muchas asignaciones pequeñas, y la sobrecarga del montón para ellas fue otra pesadilla.
Dada esa experiencia, supongo que para proyectos futuros elegiré C++ solo en sistemas de al menos 16 bits y con al menos 16 MB para RAM y almacenamiento. Ese es un límite arbitrario, y probablemente variará según cosas como el tipo de aplicación, estilos de codificación y modismos, etc. Pero dadas las advertencias, recomendaría un enfoque similar.
Hay algunas características de C++ que son útiles en los sistemas integrados. Hay otros, como las excepciones, que pueden ser costosos y cuyos costos pueden no ser siempre evidentes.
Si tuviera mis preferencias, habría un lenguaje popular que combinaría lo mejor de ambos mundos e incluiría algunas características que faltan en ambos lenguajes; algunos proveedores incluyen algunas de estas características, pero no hay estándares. Algunas cosas que me gustaría ver:
copia vacía en línea_uint32s(uint32_t *dest, const uint32_t *src, __is_const int n) { si (n <= 0) regresa; si no (n == 1) {destino[0] = src[0];} si no (n == 2) {dest[0] = src[0]; destino[1] = origen[1];} si no (n == 3) {dest[0] = src[0]; destino[1] = origen[1]; destino[2] = origen[2];} si no (n == 4) {dest[0] = src[0]; destino[1] = origen[1]; destino[2] = origen[2]; destino[3] = origen[3];} else memcpy((void*)dest, (const void*)src, n*sizeof(*src)); }Si 'n' puede evaluarse en tiempo de compilación, el código anterior será más eficiente que una llamada a memcpy, pero si 'n' no puede evaluarse en tiempo de compilación, el código generado sería mucho más grande y más lento que el código que simplemente llamado memcpy.
Sé que el padre de C++ no está muy interesado en una versión integrada de C++, pero creo que podría ofrecer algunas mejoras considerables con respecto al uso de C.
¿Alguien sabe si se está considerando algo como lo anterior para algún tipo de estándar?
uint16_t x;
que la declaración x=(uint16_t)(x*x);
debe producir un resultado mod-65536 independientemente del valor x
o el tamaño de int
. Aunque el estándar nunca ha impuesto ningún requisito cuando el tamaño int
es de 17 a 32 bits y x
es de 46341 o superior, los compiladores de 32 bits solían comportarse igual que los compiladores de 16 bits en tales casos, pero hoy en día pueden comportarse de formas extrañas y extrañas.x=(uint16_t)(1u*x*x);
sería mejor, ya que incluso un compilador hipermoderno no podrá estropearlo, no veo forma en que una especificación de lenguaje que solo requiera que los compiladores se comporte de manera consistente dado que la última redacción debe considerarse "mejor" que una que exigía que la anterior redacción debe producir los mismos resultados cuando int
es de 17 a 32 bits como lo haría para tamaños más grandes o más int
pequeños.C++ es más que un lenguaje de programación:
a) Es un "mejor" C b) Es un lenguaje orientado a objetos c) Es un lenguaje que nos permite escribir programas genéricos
Aunque todas estas funciones se pueden usar por separado, los mejores resultados se logran cuando se usan las tres al mismo tiempo. No obstante, si elige elegir solo uno de ellos, la calidad del software integrado aumentará.
a) Es una "mejor" C
C++ es un lenguaje fuertemente tipado; más fuerte que C. Sus programas se beneficiarán de esta función.
Algunas personas tienen miedo de los punteros. C++ incluye las referencias. Funciones sobrecargadas.
Y vale la pena decir: ninguna de estas características incurrieron en programas más grandes o más lentos.
b) Es un lenguaje orientado a objetos.
Alguien dijo en esta publicación que abstraer la máquina en microcontroladores no es una buena idea. ¡Equivocado! Todos nosotros, los ingenieros embebidos, siempre hemos abstraído la máquina, solo que con otra sintaxis que la de C++. El problema que veo con este argumento es que algunos programadores no están acostumbrados a pensar en objetos, por lo que no ven los beneficios de la programación orientada a objetos.
Siempre que esté listo para usar el periférico de un microcontrolador, es probable que el periférico haya sido abstraído para nosotros (usted o un tercero) en forma de controlador de dispositivo. Como dije antes, ese controlador usa la sintaxis de C, como muestra el siguiente ejemplo (tomado directamente de un ejemplo de NXP LPC1114):
/* Configuración del temporizador para coincidencia e interrupción en TICKRATE_HZ */
Chip_TIMER_Reset(LPC_TIMER32_0);
Chip_TIMER_MatchEnableInt(LPC_TIMER32_0, 1);
Chip_TIMER_SetMatch(LPC_TIMER32_0, 1, (timerFreq / TICKRATE_HZ2));
Chip_TIMER_ResetOnMatchEnable(LPC_TIMER32_0, 1);
Chip_TIMER_Enable(LPC_TIMER32_0);
¿Ves la abstracción? Entonces, cuando se usa C++ para el mismo propósito, la abstracción se lleva al siguiente nivel a través del mecanismo de abstracción y encapsulación de C++, ¡sin costo alguno!
c) Es un lenguaje que nos permite escribir programas genéricos
Los programas genéricos se logran a través de plantillas, y las plantillas tampoco tienen costos para nuestros programas.
Además, el polimorfismo estático se logra con plantillas.
Métodos virtuales, RTTI y excepciones.
Hay un compromiso cuando se utilizan métodos virtuales: mejor software frente a alguna penalización en el rendimiento. Sin embargo, recuerde que es probable que el enlace dinámico se implemente mediante una tabla virtual (una matriz de punteros de función). He hecho lo mismo en C muchas veces (incluso de forma regular), por lo que no veo inconvenientes en el uso de métodos virtuales. Además, los métodos virtuales en C++ son más elegantes.
Finalmente, un consejo sobre RTTI y excepciones: NO LOS USE en sistemas integrados. Evítalos a toda costa!!
Mi fondo, incrustado (mcu, pc, unix, otros), en tiempo real. Seguridad crítica. Presenté a un empleador anterior a STL. Ya no hago eso.
Algo de contenido de Flame
¿C++ es adecuado para sistemas embebidos?
Meh. C++ es un dolor de escribir y un dolor de mantener. C+ está más o menos bien (no use algunas funciones)
C++ en Microcontroladores? ¿RTOS? ¿Tostadoras? PC integradas?
Nuevamente digo Meh. C+ no es tan malo, pero ADA es menos doloroso (y eso es realmente decir algo). Si tienes suerte como yo, puedes hacer Java incrustado. El acceso a la matriz verificada y la aritmética sin punteros hacen que el código sea muy confiable. Los recolectores de basura en Java incorporado no son la prioridad más alta, y hay memoria de alcance y reutilización de objetos, por lo que el código bien diseñado puede ejecutarse para siempre sin un GC.
¿Es OOP útil en microcontroladores?
Seguro es. El UART es un objeto... El DMAC es un objeto...
Las máquinas de estado de objetos son muy fáciles.
¿C++ aleja demasiado al programador del hardware para ser eficiente?
A menos que sea un PDP-11, C no es tu CPU. C++ era originalmente un preprocesador encima de C, por lo que dejaría de burlarse de Bjarne Stroustrup por tener simulaciones lentas de Simula mientras estaba en AT&T. C++ no es tu CPU.
Obtenga una MCU que ejecute códigos de bytes de Java. Programa en Java. Ríete de los chicos C.
¿Debería considerarse el C++ de Arduino (sin administración de memoria dinámica, plantillas, excepciones) como "C++ real"?
No. al igual que todos los compiladores de C bastardos para MCU.
En cuarto lugar, Embedded Java o Embedded ADA están estandarizados (más o menos); todo lo demás es tristeza.
<despotricar>
Creo que C ++ es un lenguaje de mierda en primer lugar. Si quiere usar OOP, escriba programas Java. C ++ no hace nada para hacer cumplir los paradigmas OOP, ya que el acceso directo a la memoria está completamente dentro de su poder para (ab) usar.
Si tiene una MCU, lo más probable es que esté hablando de menos de 100 kB de memoria flash. Quieres estar programando en un lenguaje cuya abstracción de memoria es: cuando declaro una variable o una matriz, obtiene memoria, punto; malloc (también conocido como palabra clave "nueva" en C++) debería estar más o menos prohibido para su uso en software integrado, excepto quizás en raras ocasiones una llamada durante el inicio del programa.
Demonios, hay (con frecuencia) momentos en la programación integrada en los que C no es lo suficientemente bajo y necesita hacer cosas como asignar variables a registros y escribir ensamblaje en línea para reforzar sus rutinas de servicio de interrupción (ISR). Las palabras clave como "volátil" se vuelven bastante importantes de entender. Pasas mucho tiempo manipulando la memoria a nivel de bit , no a nivel de objeto .
¿Por qué querrías engañarte pensando que las cosas son más simples de lo que en realidad son?
</rant>
malloc (aka "new" keyword in C++) should be more or less banned from use in embedded software
--> La dulzura adicional (con intención de sarcasmo) es que new
también puede arrojar excepciones. Ahora buena suerte con eso.Los sistemas integrados están diseñados para realizar una tarea específica, en lugar de ser una computadora de propósito general para múltiples tareas. Un sistema embebido es una combinación de hardware y software de computadora. C es la madre de todas las lenguas modernas. Es un lenguaje de bajo nivel pero potente y maneja todo tipo de hardware. Por lo tanto, C/C++ es una opción óptima para desarrollar software para sistemas integrados, que es muy útil para todos los sistemas integrados. Como sabemos, C es un lenguaje de desarrollo. El sistema operativo UNIX está escrito en C. Debido a que el desarrollo de software exitoso consiste con tanta frecuencia en seleccionar el mejor lenguaje para un proyecto determinado, es sorprendente descubrir que el lenguaje C/C++ ha demostrado ser apropiado para procesadores de 8 y 64 bits. ; en sistemas con bytes, kilobytes y megabytes de memoria. C tiene el beneficio de la independencia del procesador, lo que permite a los programadores concentrarse en algoritmos y aplicaciones, en lugar de los detalles de la arquitectura del procesador en particular. Sin embargo, muchas de estas ventajas se aplican igualmente a otros lenguajes de alto nivel. ¿Pero C/C++ tuvo éxito donde tantos otros lenguajes han fallado en gran medida?
J. Polfer
toby jaffey
Kortuk
vicatcu
Kortuk
olin lathrop
brochetas