C++ für Arduino

1.) IDE wechseln. Anstatt Arduino als Plattform  auf Eclipse wechseln

  • Aktuellste Java Runtime Engine installieren
  • Aktuellste Eclipse-Version downloaden

2.) CDT (C/C++-Development-Toolkit)-Plugin hinzufügen

  • Help/Install new Software/CDT
  • MinGW (Minimalistisches GNU für Windows) installieren, damit man einen g++-Compiler hat
  • Der Ort, an dem die MinGW-Binärdatei installiert ist, muss in Eclipse in die Pfadvariable abgelegt werden
    Project/Properties/C_C++ General/Paths and Symbols -> Tab „Includes“

Funktionsnamen

Methode set: Setter
Inhalt:         Wechselt den Zustand der Variable
Struktur:    void <name>(<argument>);
Bsp:              stream.set( string );

Methode get: Getter
Inhalt:         Liest einen Wert aus
Struktur:   <type> <name>();
Bsp:              command = getToken();

Klasse Definition

class Token_stream {
    public:
        void set(string line);
        Token get();
        void put_back(Token t);
    private:
        istringstream calculation;
        bool full = false;
        Token buffer;
};

Eine Klasse beinhaltet Klassenvariablen und Klassen-Funktionen.

Instanzierung

Token_stream current_token_stream;

current_token_stream ist eine Instanz dieser Klasse. Alle Funktionen (die gebraucht werden um die Instanz zu verarbeiten) brauchen zuerst den Instanznamen.

current_token_stream.set(calculation_line);

 

Klassen-Funktionen
Jede Funktion einer Klasse muss nach der Klassendefinition definiert werden. Die Funktion braucht vor ihrem Namen die Klasse, zu der sie gehört.

void Token_stream::put_back(Token t) {
    
    // check if token is already in buffer
    if (token_buffer_full) {
        throw std::overflow_error("buffer contains already a Token");
    }
    
    // set token in buffer
    token_buffer = t;

    token_buffer_full = true;
}

Funktionen einer Klasse, müssen nicht forward deklariert werden. Sie sind durch die Definition der Klasse bereits bekannt.

Klassen-Variablen
Es gibt meist wenige Klassenvariblen. Denn diese sind „global“ innerhalb der Klasse. Alle Funktionen können auf diese Zugreifen.

class Token_stream {
    ...
    private:
        istringstream calculation;
        bool full = false;
        Token token_buffer;
};

Typisch ist, dass sie im private-Teil der Klasse stehen.

Lokale Variablen
In jeder Klassen-Funktion werden zur Verarbeitung auch lokale Variablen gebraucht.

Token Token_stream::get() {

    char c;
    Token t;

    calculation >> c;

    if (isdigit(c)) {
        t.kind = 'n';
        t.value = c;
    } else if (c == '\0') {
        t.kind = 'q';
    } 

    return t;
}

 

keyword protected

public:
Überall kann auf die Variable zugegriffen werden. In anderen Files, in anderen Funktionen, …

private:
Nur in der Klasse selbst kann auf die Variable zugegriffen werden. Innerhalb der Klassenfunktionen oder beim Instanzieren.

protected:
Von aussen kann auf diese Variable nicht zugegriffen werden. Jedoch alle abgeleiteten Objekte, können auch darauf zugreifen. Die Variable gilt nicht nur in 1 Klasse, sondern in allen abgeleiteten Klassen ebenfalls.

Abstrakte Klasse

Abstrakte Klassen enthalten keinen Code. Sie definieren nur die Schnittstelle. Sie können nicht instaziert werden!
Für die Instanzierung muss zuerst eine abgeleitete Klasse gebildet werden, welche die Schnittstellen definiert.
Abstrakte Funktionen sind ein Spezialfall bzw. der extremste Fall einer virtuellen Funktion. Sie werden pure virutal classes genannt.

// nicht instanzierbar
class B {

   public:                // können nur public sein
   virtual void f() = 0;  // = 0, damit pure ist
   virtual void g() = 0;
}

.

// abgeleitet Funktion ist instanzierbar
class B1 : public B {
  public:
      void f() override;   // kein virtual, dafür override
      void g() override;
}

B1 instanz_b;

Abstrakte Klassen sind für die Einheitlichkeit unter Code-Blöcken wichtig. Die Kompatibilität unter Klassen wird erzwungen. (Gleiche Abhandlungen heissen in allen Varianten gleich. )

binary output C++

Problem mit << (Standard filestream Operator)

Mit Bitset und << in einen Outputfilestream wurde jedes 0 und jedes 1 zu einem Ascii gewandelt.

std::bitset<16> left (161);
std::bitset<8> symbol(98);

std::ofstream ofs;
ofs.open("treebinary.bin", std::ofstream::binary) 

ofs << left << symbol ;

ofs.close()

Dateigrösse (16 + 8) * 1 Char = 24 Byte. (Faktor 8 zu gross).

Lösung write( Pointer to Data, size)
In der Funktion write kann ein Pointer übergeben werden. Dadurch findet keine Umwandlung in String (durch <<) statt. Die 0 und 1er werden direkt binär geschrieben.

char data[3]    // 3 Bytes

// inhalt von 1 Byte und 2 Bytes
std::bitset<16> left (361);
std::bitset<8> symbol(98);

// array füllen
data[0] = left;
data[2] = symbol;

//array in 1mal ausgeben
std::ofstream ofs; ofs.write((char *) data , 3) 

ofs.close()
  • Alle Bits werden auf einmal geschrieben
  • kein << left << symbol notwendig
  • Man gibt den Pointer mit und die Länge der Daten
char* data[3]    // initalisiert als Pointer

std::bitset<16> left (361);
std::bitset<8> symbol(98);

data[0] = left;
data[2] = symbol;

std::ofstream ofs; ofs.write(data , sizeof() 

ofs.close()
// array of struct
std::struct Node
{  
    uint16_t left = 0;
    uint16_t right = 0;
    uint8_t symbol = 0;
    bool filled = false;
};

Node tree[100];

// füllen des arrays per tree-funktion


// array per Pointer mitgeben, länge = Node-länge * Anzahl
std::ofstream ofs; ofs.write((char*) tree , sizeof( (struct Node) * 100);

ofs.close()

Klasse aufbauen

Die Klasse dient zum spezifischen Verarbeiten eines Objektes.

Das Objekt wird mit em Konstruktor gebildet Klasse::Klasse()

klasse

Um auf das Objekt zuzugreifen, es zu verändern oder den Zustand seiner Variablen zu kennen braucht man Klassen-Funktionen Klasse::funktion() .

Auf alle Klassenvariablen kann man nur über das Objekt zugreifen objekt.variable .

Bsp Huffman Map
– Konstruktor bildet die Map- hm.readToken(„stream.txt“)  Daten werden von aussen eingegeben und lokal im Objekt gespeichert
– hm.getBitValue()    Lokale Varibale wird verarbeitet und ausgegeben
– hm.getZeros()         Andere Variable wird verabeitet und ausgegeben

 

per Reference C++, per Pointer C, C++

Grund
Will man mehrere externe Variable in einer Funktion bearbeiten, hat aber nur einen return Wert, so gibt man die Adressen dieser Variablen als Paramter mit. Dadurch wird die Variable direkt durch die Funktion bearbeitet. (Die Alternative wäre, globale Variablen zu definieren und diese zu übergeben. Da globale Varibalen vermieden werden sollen, entfällt diese Alternative.)

Pointer übergeben (C und C++)

function(int *x){do someting}    // Deklaration: Parameter ist ein Pointer

int a ;
function(&a);                    // Argument: Adresse der Variable

Bei der Übergabe per Pointer, wird keine lokale Kopie des Arguments erstellt, sondern die Änderungen werden direkt in den Speicher geschrieben.

Per Reference übergeben (C++)

function(int &x);           // Deklaration: Parameter einer Variable per Ref.
int a;
function(a);                // Die Adresse des Arguments wird genommen

Bei per Reference geschieht das selbe wie beim Pointer. Nur sieht man es beim Argument nicht direkt. Die Variable wird „normal“ übergeben.
Trotzdem ist die Wirkung die selbe wie bei der Pointerübergabe: Wie bei einer gobalen Varibale wird durch die Funktion die externe Variable verändert (LINK)

ASCII to binary

Wird einem char eine Zahl zugewiesen, so entspricht dies nicht der binären Darstellung !

int a = 16            0001 0000
char b = ‚16′         0011 0001    0011 0110     sind zwei Ascii-Zeichen
char c = ‚1‘            0011 0001
char d = ‚6‘           0011 0110
int d = 6             0000 0110

Lösung
char c = ‚1‘ – ‚0‘    0000 0001
Will man einen Char, der die Zahl binär darstellt, so muss man den char-Wert ‚0‘ abziehen. Die Hochkommas sind zwingend, da sonst ein Typcast (zu int) vollzogen wird.

c++ Operator overloding

Kapitel 9: Operatoren nicht nur für Integers
(Übung Buchhandlung mit Christian: == für den Vergleich der Klasse Buch)

class Book {
private:
    string ISBN;
    string title;
    string author;
public: 
    Book(string isbn, string t, string a){ 
        ISBN = isbn; 
        title = t; 
        author = a; 
}

// buch1 == buch2

bool operator==(const Book& b2)       // new Datatyp is Book
  {
    if(ISBN == b2.ISBN)               // this new Typ is addresd by b2
      return true;
    else
      return false;
  }
Book buch1 = Book{978-8-123-12345-6, "Andorra", "Frisch"};
Book buch3 = Book{978-8-123-12345-6, "Mein Name ist Gantenbein", "Frisch"};
if (buch1 == buch3)
    cout << "Die Bücher sind gleich." << endl;
  else
    cout << "Die Bücher sind nicht gleich." << endl;

Hier sind buch1 und buch3 gleich, weil sie die gleichen ISBN haben.

 

Kapitel 10: IO
Der Operator << ist ursprünglich nur für String.
Sollen bei << auch andere Datentypen übergeben werden können, braucht es ein Overloading.

ostream& operator <<(ostream& os, const Date& d)

Ab sofort kann auch die Klasse Date dem stream übergeben werden.

 

 

 

 

..