OOP vs Inline con Arduino


8

Sto programmando da un po 'di tempo, ma sono nuovo di Arduino e AVR. La domanda principale che ho sulla programmazione di questi microcontroller è che ci sono grandi differenze nella progettazione del codice nelle classi orientate agli oggetti rispetto alla programmazione in linea più tradizionale che ho visto in molti esempi?

In altre parole, nel mondo dei controller Arduino / AVR ci sono risparmi con memoria e prestazioni che utilizzano classi o viceversa?

Supponiamo ad esempio che abbiamo una classe:

class SomeClass(){

private:
   int x;
   int y;

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

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

Ci sarebbero prestazioni o guadagni di memoria nel progettare il programma in modo più integrato come:

int x;
int y;

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

Ho provato a fare alcune ricerche su Stack Exchange e Google ma non sono riuscito a trovare la risposta che sto cercando la cosa più vicina che sono riuscito a trovare è stata questa domanda di scambio di stack.

Il motivo per cui sto chiedendo questo è che ho un progetto che deve essere il più leggero possibile e non sono chiaro come dovrei progettare il mio programma in questo ambiente.


modificare

Grazie per le risposte, questo ha fatto luce sulle cose. C'è una cosa di cui non sono abbastanza chiaro.

Supponi di avere una classe che stai progettando che utilizza u8glib come segue:

class UserInterface{
private:
   U8GLIB_ST7920_128X64 Display;

public:
   UserInterface();
}

Come ti muoveresti usando "Dynamic Memory" come:

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

Risposte:


2

La domanda principale che ho sulla programmazione di questi microcontroller è che ci sono grandi differenze nella progettazione del codice nelle classi orientate agli oggetti rispetto alla programmazione in linea più tradizionale che ho visto in molti esempi?

...

In altre parole, nel mondo dei controller Arduino / AVR ci sono risparmi con memoria e prestazioni che utilizzano classi o viceversa?

Sì, c'è una grande differenza tra l'uso di C o C ++ per sistemi embedded su piccola scala come Arduino / AVR. C ++ consente di fornire ulteriori informazioni per l'ottimizzazione del compilatore.

Se stai implementando un framework OOP, la piattaforma o il runtime C ++ e le classi possono anche aiutarti con l'architettura e il riutilizzo del software. In Cosa vengono utilizzati numerosi modelli di progettazione OOP per ottenere interfacce sia per i programmatori di applicazioni che per i programmatori di driver di dispositivo. La più comune è la delega .

L'uso di classi astratte, funzioni dei membri virtuali, allineamento e modelli può aiutare a ottenere impronte più basse e prestazioni più elevate rispetto alle implementazioni tradizionali. Ad esempio, le classi Cosa Pin sono X5-X10 più veloci del core Arduino e allo stesso tempo più piccole nel footprint. Si prega di consultare i parametri di riferimento .

Una cosa da "disimparare" dalla tradizionale programmazione C ++ è l'uso di new / delete (malloc / free). Con una dimensione SRAM di solo pochi Kbyte l'utilizzo di un heap rappresenta un rischio. La risposta sono classi statiche e dati basati su stack.

C'è molto altro da dire sull'architettura del framework OOP, ma spero che questo aiuti a rispondere alle tue domande iniziali.

Saluti!


Bella risposta! Ho aggiornato la mia domanda chiedendomi come aggirare la memoria dinamica (new / delete / malloc / free). Hai qualche input sul non usare l'allocazione dinamica della memoria? Tutto ciò che deve essere condiviso durante le lezioni dovrebbe essere globale? Non suona bene per me, mi è stato sempre insegnato a non usare i globali se puoi aiutarlo.
Andy Braham,

Un breve commento sul tuo esempio UserInterface sopra. Il display è in realtà la composizione di oggetti (non riferimento / puntatore) quindi non è necessario nuovo. È necessario avviare il display. La costruzione UserInterface dovrebbe assomigliare a questa. UserInterface::UserInterface() : Display(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE) { ... }. I parametri necessari del costruttore Display devono essere passati al costruttore UserInterface.
Mikael Patel,

OOP è tutto incapsulato, nascondere i dati, quindi la condivisione dovrebbe essere un minimo (zero). L'obiettivo è avere più o meno solo un numero di oggetti statici globali che interagiscono. Trattengono e nascondono lo stato globale. Per gli oggetti i dati dei membri sono uno stato locale non dinamico. Per raggiungere l'obiettivo è necessario ancora una serie di trucchi; trasformazioni di programma.
Mikael Patel,

Un esempio; Considera la progettazione di una classe BitSet che può avere un numero variabile di membri. La soluzione ovvia è calcolare il numero di byte richiesti e utilizzare new / malloc. Un'alternativa è passare l'archiviazione come parametro al costruttore BitSet. Una terza alternativa è una classe modello con numero di elementi come parametro. Ciò consente una variabile membro che è il numero necessario di byte. Vedere Cosa / BitSet.hh per maggiori dettagli su questa variante di trasformazione del programma. Ci sono più trasformazioni.
Mikael Patel,

Ho aggiornato il mio esempio UserInterface, è più o meno l'approccio corretto? Penso di avere una buona conoscenza di come implementare ciò di cui ho bisogno.
Andy Braham,

4

Il motivo per cui non riesci a trovare la risposta è perché la risposta è sia Sì che No.

Per le cose di base della classe - definire la tua classe con metodi ecc. E istanziare oggetti da essa - c'è poca differenza nel risultato finale rispetto alla "vaniglia" C. Le ottimizzazioni del compilatore sono così buone ora che le prestazioni sono uguali. Sì, potrebbero esserci lievi aumenti nell'uso della memoria poiché si passa un puntatore extra con ogni chiamata di metodo (invece di foo(int x)te foo(MyClass *this, int x)) ma è così piccolo da non essere evidente.

Le grandi differenze arrivano quando inizi a giocare con il polimorfismo e altri argomenti avanzati. Quando inizia a fare questi programmi complessi, il compilatore non è sempre in grado di capire quali funzioni sono richieste e quali no, e non è in grado di eliminare le funzioni inutilizzate ( "garbage collection" ). Quindi potresti finire con un codice più grande.

Ciò non significa un codice più lento, ma solo blocchi di codice che sono in giro che non fanno mai nulla.

Di maggiore importanza è gestire la memoria dinamica meglio di quanto si è abituati. Poiché esiste una quantità così piccola di memoria, l'heap è molto piccolo e, di conseguenza, viene frammentato molto facilmente. Creazione dinamica e la distruzione di oggetti ( new myClass, delete myClassObject, ecc) è molto cattivo. Gli oggetti di classe devono davvero essere definiti staticamente (nell'ambito globale è il più comune) o temporaneamente allocati nello stack (istanze locali). Altrimenti stai chiedendo problemi - e il primo che saprai è che accadono cose strane (nessuna segnalazione di errore o eccezioni, vedi ...).


Va bene, quindi se lo capisco correttamente in questo mondo della programmazione, le classi vengono utilizzate per un ruolo più organizzativo e di portabilità rispetto al vero OOP e che le cose dovrebbero essere allocate staticamente ed esplicitamente piuttosto che dinamicamente. Grazie, questo ha molto senso e spiega perché ho visto degli esempi e cosa non è stato scritto così.
Andy Braham,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.