¿Cómo puede hacer C ++ cuando su comstackdor incorporado no tiene operador nuevo o soporte STL?

Estoy trabajando en un proyecto de grupo para personas de la tercera edad para mi universidad y me he encontrado con un gran obstáculo al intentar que mi código funcione.

El comstackdor que tenemos para nuestro microcontrolador Atmel de 8 bits no es compatible con los operadores nuevos o eliminados, y no es compatible con C ++ STL. Podría progtwigrlo en C, pero tengo que implementar un algoritmo A * que nunca había hecho antes. Mientras probé C inicialmente, pronto me di cuenta de que nunca había hecho C pura. Tratar de modelar objetos con estructuras y funciones me está ralentizando, ya que estoy acostumbrado a la syntax de C ++ mucho más clara.

En cualquier caso, la redacción exacta de los defectos de mis comstackdores se puede encontrar aquí: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

Para superarlos y seguir utilizando C ++ he considerado las siguientes posibilidades. 1) No asigne nada, solo use plantillas para generar arreglos fijos en la stack. 2) Asigne y encuentre algún truco para llamar al constructor para objetos una vez que les haya asignado el espacio. La nueva colocación no es una opción, ya que la nueva no es un operador. 3) Solo usa C y chúpalo, es un microcontrolador ¿por qué me estoy volviendo elegante? 4) Encuentra un mejor comstackdor que probablemente cueste $$$.

La segunda opción es la más difícil, pero tendría la mayor recompensa en términos de cómo puedo escribir este código. Sin embargo, me imagino que depurarlo podría ser un gran dolor si me equivoco. Estoy pensando en crear objetos en la stack, copiar sus bits en el espacio asignado y luego poner a cero los bits en el objeto para que no llame a su destructor. Para hacer eso, accedería a los bits directamente con un puntero de carácter sin signo y el operador sizeof para obtener el recuento de bytes.

Eso suena terrible y no sé si podría funcionar de manera confiable, pero lo estoy considerando. Sé que vtables puede ser un problema, pero no pretendo tener vtables, ya que es solo un microcontrolador de 8 bits.

Solo para el registro, poner a cero los bits en un objeto no afectará si se llama al destructor (a menos que el comstackdor tenga una peculiaridad especial que permita este comportamiento). Solo escribe algunas declaraciones de registro en tu destructor para probar esto.

Estructurar su progtwig para no asignar nada es probablemente la forma en que se diseñó el sistema. No he trabajado con sistemas integrados anteriormente, sin embargo, he leído algunas tiendas integradas con experiencia que desalientan el uso de la memoria dinámica porque el entorno de ejecución tiene pocas cantidades.


Sin embargo, si es necesario, todavía puede utilizar la ubicación nueva. Si no tiene el encabezado , aquí están las líneas relevantes directamente en mi versión de GCC:

 // Default placement versions of operator new. inline void* operator new(std::size_t, void* __p) throw() { return __p; } inline void* operator new[](std::size_t, void* __p) throw() { return __p; } // Default placement versions of operator delete. inline void operator delete (void*, void*) throw() { } inline void operator delete[](void*, void*) throw() { } 

Pegue eso en algún lugar en un archivo de encabezado incluido en cada archivo fuente que use la ubicación nueva / eliminar.

Archivo de muestra que prueba esto:

 #include  #include  int main(int argc, char** argv) { typedef char const* cstr; char foobar[16]; cstr* str = new (&foobar) cstr(argc > 1 ? argv[1] : "Hello, world!"); std::puts(*str); str->~cstr(); } 

En mi versión de GCC, esto no usa libstdc++ en absoluto (si se -fno-exceptions ).


Ahora, si desea combinar eso con malloc (si su plataforma lo proporciona), puede hacer esto:

 #include  #include  inline void* operator new (std::size_t n) {return std::malloc(n);} inline void* operator new[](std::size_t n) {return std::malloc(n);} inline void operator delete (void* p) {std::free(p);} inline void operator delete[](void* p) {std::free(p);} int main(int argc, char** argv) { typedef char const* cstr; cstr* str = new cstr(argc > 1 ? argv[1] : "Hello, world!"); std::puts(*str); delete str; } 

Esto le permite usar el new / delete estándar con el que está familiarizado, sin requerir el uso de libstdc++ .

¡Buena suerte!

No pelees con tus herramientas. Si el único comstackdor que tiene para su sistema integrado es un comstackdor de C, aprenda C, no es difícil. Tratar de producir una versión bastardizada de los dos lenguajes solo para resolver un problema de progtwigción bastante simple solo terminará en lágrimas.

Para verlo de otra manera, si su plataforma integrada no fuera compatible con un comstackdor de C, sino solo con un ensamblador, ¿su primer impulso sería sentarse y escribir un comstackdor de C ++ en ensamblador? Espero que no, espero que se siente y aprenda a usar el ensamblador para completar su tarea: escribir un comstackdor de C ++ (o incluso un comstackdor de C) sería un uso totalmente inadecuado de su tiempo y casi con seguridad resultaría en un fracaso.

Creo que te estás acercando al problema desde un punto de vista que no es óptimo.

Se está enfocando en el comstackdor (o en su ausencia) en lugar de enfocarse en el HARDWARE.

La respuesta más probable a sus preguntas principales es “porque el hardware no admite todas esas cosas de C ++”. El hardware integrado (microcontroladores) se destaca por la personalización del diseño del hardware: mapas de memoria, controladores de interrupciones, E / S, etc.

En mi opinión, primero debe pasar algún tiempo con el libro de hardware para el microcontrolador, aprendiendo los entresijos del dispositivo, es decir, cómo se diseñó y con qué propósito principal. Algunos se diseñaron para una rápida manipulación de la memoria, otros para un rápido manejo de E / S, otros para trabajos de tipo A / D y otros para procesamiento de señales. El tipo de microcontrolador dicta las instrucciones del ensamblador que escribieron para él, y eso dicta lo que cualquier comstackdor de nivel superior puede hacer de manera eficiente.

Si esto es importante, tómate un tiempo para mirar también al ensamblador: te dirá lo que los diseñadores consideraron importante. También le dirá mucho sobre cuánto puede obtener de un comstackdor de alto nivel.

En general, los microcontroladores no admiten C ++ porque al diseño realmente no le importan los objetos ni el manejo de memoria sofisticado (desde la perspectiva de C ++). Se puede hacer, pero a menudo se intenta golpear una clavija redonda en un agujero cuadrado para que los constructores y destructores (y ‘nuevo’ y ‘eliminar’) funcionen en el microentorno.

Si tiene un comstackdor de C para esta unidad, considérelo como una bendición. Un buen comstackdor de C es a menudo “más que suficiente” para crear un excelente software integrado.

Aclamaciones,

-Ricardo

El hecho de que no tenga estas herramientas no significa que no pueda beneficiarse de C ++. Si el proyecto es lo suficientemente grande, el acceso solo al diseño orientado a objetos podría ser suficiente motivación.

Si no es compatible con ‘nuevo’, es probable que sea porque no tiene sentido hacer una distinción automática entre un montón y la stack. Esto podría ser debido a su configuración de memoria. También podría deberse a que los recursos de memoria están tan limitados que solo tiene sentido una asignación muy cuidadosa. Si absolutamente tiene que implementar su propio ‘nuevo’ operador, podría considerar adaptar el malloc de Doug Lea . Creo que comenzó su asignador en una circunstancia similar (reimplementando el nuevo de C ++).

Me encanta el STL pero todavía es posible hacer cosas útiles sin él. Dependiendo del scope del proyecto, es posible que sea mejor usar una matriz.

Tuve un comstackdor similar que implementó una versión extraña del estándar Embedded-C ++ . Teníamos un operator new que llamaría constructores para nosotros y los destructores se llamaban en la mayoría de los casos . El proveedor del comstackdor / tiempo de ejecución implementó el método try and catch utilizando setjmp y longjmp como una conveniencia para el ingeniero . ¡El problema fue que nunca mencionaron que un throw no provocaría que se invocaran destructores de objetos locales!

De todos modos, nuestro grupo heredó el código base después de que alguien escribiera una aplicación que actuaba como si fuera Standard C ++: usando técnicas RAII y todo lo demás. Terminamos reescribiéndolo en lo que muchos de nosotros llamamos C orientado a objetos . Es posible que desee considerar simplemente morder la viñeta y escribir en línea recta C. En lugar de constructores, tenga un método de inicialización llamado explícitamente. Los destructores se convierten en un método de terminación llamado explícitamente. No hay mucho de C ++ que no puedas imitar en C bastante rápido. Sí, MI es un dolor en el … pero la herencia individual es bastante fácil. Echa un vistazo a este PDF para algunas ideas. Casi describe el enfoque que tomamos. Realmente desearía haber escrito nuestro método en algún lugar …

Puede encontrar algún código útil en mi sitio web A * tutorial . Aunque el código que escribí para admitir esto usa STL, debería ser fácil de quitar el soporte de STL. Además, se incluye un asignador de grupo (fsa.h) que escribí para acelerar STL en las consolas de juegos. Es un código C ++, pero lo porté originalmente desde C y no creo que sea difícil hacerlo de otra manera. El código es probado por más de 10,000 personas, por lo que es una buena base para comenzar.

Reemplazar las estructuras STL que estoy usando no es un problema ya que está limitado a Vectores. Utilizo uno de los vectores como una cola de prioridad usando las funciones de montón (make_heap y push_heap). Puede reemplazarlo con mi antiguo código C, que tiene una cola de prioridad implementada en C que solo debería caer en su código. (Lo que solo hace una asignación, por lo que puede reemplazarlo con un puntero a un área reservada de su memoria).

Como puede ver en este fragmento de código del encabezado, la principal diferencia en el código C es que no hay este puntero, ningún objeto, por lo que su código normalmente toma un puntero del objeto como primer argumento.

 void PQueueInitialise( PQUEUE *pq, int32 MaxElements, uint32 MaxRating, bool32 bIsAscending ); void PQueueFree( PQUEUE *pq ); int8 PQueuePush( PQUEUE *pq, void *item, uint32 (*PGetRating) ( void * ) ); int32 PQueueIsFull( PQUEUE *pq ); int32 PQueueIsEmpty( PQUEUE *pq ); void *PQueuePop( PQUEUE *pq, uint32 (*PGetRating) ( void * ) ); 

¿Por qué no escribirlo primero en su computadora de escritorio, teniendo en cuenta las limitaciones del comstackdor, depurarlo, asegurarse de que funciona perfectamente y solo luego pasar al entorno integrado?

cuando realizaba trabajo integrado, una vez ni siquiera podía vincular el tiempo de ejecución de C para las limitaciones de memoria, pero el hardware tenía una instrucción DMA (asignador de memoria dinámica), así que escribí mi propio malloc con ese hardware, es probable que su hardware tenga una característica similar, así que Podrías escribir un malloc y luego un nuevo basado en el malloc.

De todos modos, al final, utilicé 99% de las asignaciones de stack, y algunos límites establecen los objetos estáticos que reciclaría, al construir en su lugar. Esto podría ser una buena solución.