OOP vs Inline con Arduino

8

He estado programando durante bastante tiempo, pero soy nuevo en Arduino y AVR Programming. La pregunta principal que tengo sobre la programación de estos microcontroladores es si existen diferencias importantes en el diseño de código en clases orientadas a objetos en comparación con la programación en línea más tradicional que he visto en muchos ejemplos.

En otras palabras, en el mundo de los controladores Arduino / AVR, ¿hay algún ahorro con la memoria y el rendimiento al utilizar clases o viceversa?

Digamos por ejemplo que tenemos una clase:

class SomeClass(){

private:
   int x;
   int y;

public:
   void foo();
   void bar();
}

SomeClass thisClass;
thisClass.foo();
thisClass.bar();

¿Habría algún rendimiento o ganancia de memoria al diseñar el programa de una manera más en línea como:

int x;
int y;

void foo(){ /*** Do something ***/};
void bar(){ /*** Do more stuff ***/};

Intenté hacer algunas búsquedas en Stack Exchange y Google, pero no pude encontrar la respuesta que estoy buscando. Lo más parecido que pude encontrar fue esta pregunta de Stack Exchange.

La razón por la que pregunto sobre esto es porque tengo un proyecto que debe ser lo más ligero posible y no tengo claro cómo debería diseñar mi programa en este entorno.


Editar

Gracias por las respuestas, esto ha arrojado luz sobre las cosas. Hay una cosa que no tengo muy claro.

Digamos que tiene una clase que está diseñando que utiliza u8glib de la siguiente manera:

class UserInterface{
private:
   U8GLIB_ST7920_128X64 Display;

public:
   UserInterface();
}

¿Cómo se movería usando "Memoria dinámica" como:

UserInterface::UserInterface(){
   UserInterface::Display = U8GLIB_ST7920_128X64(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE);
}
Andy Braham
fuente

Respuestas:

2

La pregunta principal que tengo sobre la programación de estos microcontroladores es si existen diferencias importantes en el diseño de código en clases orientadas a objetos en comparación con la programación en línea más tradicional que he visto en muchos ejemplos.

...

En otras palabras, en el mundo de los controladores Arduino / AVR, ¿hay algún ahorro con la memoria y el rendimiento al utilizar clases o viceversa?

Sí, hay una gran diferencia entre usar C o C ++ para sistemas integrados a pequeña escala como Arduino / AVR. C ++ permite que se proporcione más información para las optimizaciones del compilador.

Si está implementando un marco OOP, plataforma o tiempo de ejecución, C ++ y las clases también pueden ayudarlo con la arquitectura y la reutilización del software. En Cosa , se utilizan varios patrones de diseño de OOP para lograr interfaces tanto para programadores de aplicaciones como para programadores de controladores de dispositivos. Lo más común es la delegación .

El uso de clases abstractas, funciones de miembros virtuales, alineación y plantillas puede ayudar a lograr una huella más baja y un mayor rendimiento que las implementaciones tradicionales. Como ejemplo, las clases de Pin Cosa son X5-X10 más rápidas que el núcleo Arduino y al mismo tiempo más pequeñas en huella. Por favor vea los puntos de referencia .

Una cosa para "desaprender" de la programación tradicional de C ++ es el uso de new / delete (malloc / free). Con un tamaño de SRAM de solo unos pocos Kbyte, usar un montón es un gran riesgo. La respuesta es clases estáticas y datos basados ​​en pila.

Hay mucho más que decir sobre la arquitectura de marco de OOP, pero espero que esto ayude a responder sus preguntas iniciales.

¡Salud!

Mikael Patel
fuente
¡Buena respuesta! Actualicé mi pregunta preguntando cómo moverse por la memoria dinámica (nuevo / eliminar / malloc / gratis). ¿Tiene alguna entrada para no usar la asignación de memoria dinámica? ¿Debería todo lo que necesita ser compartido a través de las clases ser global? Eso no me suena bien, siempre me enseñaron a no usar globales si puedes evitarlo.
Andy Braham
Un breve comentario sobre su ejemplo de UserInterface anterior. La pantalla es en realidad composición de objetos (no referencia / puntero), por lo que no necesita nuevos. Necesita iniciar la pantalla. La construcción de la interfaz de usuario debería verse así. UserInterface::UserInterface() : Display(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE) { ... }. Los parámetros del constructor Display necesarios deben pasarse al constructor UserInterface.
Mikael Patel
OOP tiene que ver con la encapsulación, la ocultación de datos, por lo que compartir debe ser mínimo (cero). El objetivo es tener más o menos una cantidad de objetos estáticos globales que interactúen. Mantienen y ocultan el estado global. Para los objetos, los datos del miembro son un estado local no dinámico. Para lograr el objetivo, necesita nuevamente un conjunto de trucos; transformaciones de programa.
Mikael Patel
Un ejemplo; Considere el diseño de una clase BitSet que puede tener un número variable de miembros. La solución obvia es calcular la cantidad de bytes necesarios y usar new / malloc. Una alternativa es pasar el almacenamiento como parámetro al constructor de BitSet. Una tercera alternativa es una clase de plantilla con número de elementos como parámetro. Esto permite una variable miembro que es el número necesario de bytes. Consulte Cosa / BitSet.hh para obtener más detalles sobre esta variante de transformación del programa. Hay más transformaciones.
Mikael Patel
Actualicé mi ejemplo de UserInterface, ¿es más o menos el enfoque correcto? Creo que ahora entiendo muy bien cómo implementar lo que necesito.
Andy Braham
4

La razón por la que no puede encontrar la respuesta es porque la respuesta es Sí y No.

Para cosas básicas de clase, definiendo su clase con métodos, etc. e instanciando objetos a partir de ella, hay poca diferencia en el resultado final en comparación con "vanilla" C. Las optimizaciones del compilador son tan buenas ahora que el rendimiento es el mismo. Sí, puede haber un ligero aumento en el uso de la memoria ya que pasa un puntero adicional con cada llamada al método (en lugar de lo foo(int x)que tiene foo(MyClass *this, int x)), pero eso es tan pequeño que no se nota.

Las grandes diferencias surgen cuando comienzas a jugar con polimorfismo y otros temas avanzados. Cuando comienza a hacer estos programas complejos, el compilador no siempre es incapaz de determinar qué funciones son necesarias y cuáles no, y no puede eliminar las funciones no utilizadas ( "recolección de basura" ). Por lo tanto, puede terminar con un código más grande.

Eso no significa un código más lento, solo fragmentos de código que están dando vueltas y que nunca hacen nada.

De mayor importancia es administrar su memoria dinámica mejor de lo que está acostumbrado. Como hay una cantidad tan pequeña de memoria, el montón es muy pequeño y, como resultado, se fragmenta muy fácilmente. Creación y destrucción de objetos (dinámico new myClass, delete myClassObject, etc) es muy malo. Los objetos de clase realmente necesitan estar estáticamente definidos (en el ámbito global es el más común) o asignarse temporalmente en la pila (instancias locales). De lo contrario, está buscando problemas, y lo primero que sabrá es que están sucediendo cosas extrañas (no hay informes de errores ni excepciones, ya ve ...).

Majenko
fuente
Bien, si entiendo esto correctamente en este mundo de programación, las clases se usan para un rol más organizativo y de portabilidad que OOP verdadero y que las cosas deberían asignarse estática y explícitamente en lugar de dinámicamente. Muchas gracias, esto tiene mucho sentido y explica por qué he estado viendo ejemplos y lo que no está escrito de la manera en que están.
Andy Braham