mikrocontroller.net

Forum: Compiler & IDEs C++ Arduino Grundlagen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier gibt es eine Anleitung für C++ Klassen:
http://www.cplusplus.com/doc/tutorial/classes/

Was spricht eigentlich dagegen, nur *.h-Files anstatt *.cpp files zu 
verwenden ? BTW die Methodendeklaration zusammen zu halten?

Hier mein Beispiel:
class Test
{
  public:
    void begin()
    {
      Serial.begin(115200);
    }
    void update()
    {
      Serial.println("update");
    }

};


Test tt;

void setup()
{
  tt.begin();
}

void loop()
{
  tt.update();
  delay(100);
}

: Verschoben durch Moderator
Autor: Jim M. (turboj)
Datum:

Bewertung
-4 lesenswert
nicht lesenswert
Cornelius schrieb:
> Was spricht eigentlich dagegen, nur *.h-Files anstatt *.cpp files zu
> verwenden

Man kann so kein Makefile vernünfitg einsetzen.

Denn ein "normales" Makefile kann mit wenig Aufriss alle *.cpp durch den 
Compiler jagen und alle *.h ignorieren. Und ja, das gehört genau so.

Bei IDEs ist es ähnlich, sobald sie Features wie Syntax Highlighting 
haben. Auch da muss man IMO *.h und *.cpp unterschiedlich behandeln - 
sonst hagelt es Phantomfehler.

Autor: Vincent H. (vinci)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Der imho einzige Grund ist, dass das Compilieren von *.c Datein 
parallelisierbar ist.

Autor: S------- R. (simonr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übersichtlichkeit und noch weit mehr

Ich verwende den STM32 mit C++ und benutze Sachen wie virtuelle 
abstrakte Klassen (Interfaces) und andere Späße.

manchmal ändert sich die Implementierung der Klasse während die 
Funktionsnamen und Parameter die Selben geblieben sind.

Beispielsweise habe ich eine virtuelle Klasse "externer ADC". Das heisst 
wenn es einen neuen ADC gibt welcher besser geeignet ist, dann muss ich 
nur eine .cpp neu schreiben und das wars.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich mir C# oder Java anschaue, brauch ich da auch keine getrennten 
Declarations- und Implementationsfiles.
Es erscheint mir umständlich.

Hier mein minimalistisches LED-Experiment ohne Trennung:

class Led
{
  boolean ledState;
  unsigned char ledPin;

  public:
    Led(unsigned char _ledPin)
    {
      ledState=false;
      ledPin=_ledPin;
      pinMode(ledPin,OUTPUT);
    }

    void toogle()
    {
      ledState=!ledState;
      digitalWrite(ledPin,ledState);
    }
};

Led led1(LED_BUILTIN);

void setup()
{

}

void loop()
{
  led1.toogle();
  delay(100);
}

Als will ich ein Led-Array daraus machen. Alledings klapts leider noch 
nicht ganz.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Stell Dir vor, du hast eine hardware.h mit der Funktion led_an(). Nun 
bindest du diese Header Datei in zwei c oder cpp Dateien ein. Dann 
existiert die Funktion doppelt, und das darf nicht sein.

Der Sinn Header Dateien ist, dem Compiler anzukündigen, wie eine 
Struktur oder eine Funktion von außen betrachtet aussieht. Du kannst sie 
mit Spezifikationen von Bauteilen vergleichen.

Mit Hilfe der Spezifikation eines Mikrochips kannst du eine Platine 
bauen, in die der Mikrochip später eingesetzt werden kann. Die 
Spezifikation beschreibt die Abmessungen, die Eingänge und die Ausgänge 
des Mikrochips.

Später bestückst du die Platine mit dem Mikrochip. Du brauchst seinen 
Innenschaltplan nicht zu kennen, Hauptsache er erfüllt die 
Spezifikation.

So ähnlich ist das mit dem C/C++ Compiler. Wenn er eine C/C++ Datei 
compiliert kann er Funktionsaufrufe einplanen, ohne dass der Code für 
die Funktion zu diesem Zeitpunkt vorliegt. Hauptsache, Aufrufparameter 
und Rückgabewert passen. Und die Header Datei sagt dem Compiler, wie 
diese aussehen sollen, das ist die Spezifikation.

Wenn der Linker am Ende die Fragmente deines Programmes (*.o Dateien) 
zusammenfügt, muss alles vollständig sein. Wenn nach dem Zusammenfügen 
noch irgendeine Funktion fehlt, meldet sich der Linker entsprechend.

Außerhalb der Mikrocontroller Welt werden Libraries typischerweise in 
bereits compilierter Form ausgeliefert. Bei Linux sind das entweder *.o 
oder *.so Dateien, bei Windows sind es normalerweise *.dll Dateien.

Die *.o Dateien sind Code-Fragmente, die der C Compiler zu einem ganzen 
Programm zusammenfügen kann. Die *.so und *.dll Dateien werden hingegen 
zur Laufzeit vom Betriebssystem nachgeladen - sind daher für µC eher 
irrelevant.

In der Regel werden Libraries zusammen mit Header Dateien ausgeliefert, 
damit man sie in selbst geschriebene C/C++ Quelltexte einbinden kann. 
Der C-Compiler braucht sie, sonst kann er nicht wissen, welche 
"Signatur" (Eingabe- und Ausgabeparameter) die Funktion hat.

Also: *.h Dateien enthalten Spezifikationen von Funktionen und 
Datentypen, aber keinen konkreten Quelltext der Speicher belegen würden.

*.c Dateien Enthalten konkreten Quelltext und konkrete Variablen, die 
Speicher belegen. Der Compiler erzeugt daraus *.o Dateien.

Viele *.o Dateien werden durch den Linker zu einem lauffähigen Programm 
zusammengefügt.

Autor: Keiner N. (nichtgast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Wenn ich mir C# oder Java anschaue, brauch ich da auch keine getrennten
> Declarations- und Implementationsfiles.
> Es erscheint mir umständlich.

Tja, Äpfel,Birnen und so.

Ich persönlich finde es auch besser, wenn man diesen include quatsch 
nicht brauchen würde. C# und Java verfolgen aber ein Grundlegend anderes 
Konzept im Hintergrund (beide werden eigentlich eher interpretiert als 
compiliert. Gut, so einfach ist dass dann auch wieder nicht).

Bei C/C++ ist es nun mal so, dass die Deklaration im Headerfile erfolgt 
und die Implementation im Codefile. Das hat den Vorteil, dass man die 
Codefiles nur einmal Compilieren muss, wenn sich was ändert. Ansonsten 
wird das einfach nur vom Linker hinzugefügt.

Wenn du die Implementation im Headerfile machst, dann muss der Compiler 
jedes mal alles compilieren. Das würde ewig dauern und echt schnell 
nerven.

: Bearbeitet durch User
Autor: Stefanus F. (stefanus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Cornelius schrieb:
> Wenn ich mir C# oder Java anschaue, brauch ich da auch keine
> getrennten Declarations- und Implementationsfiles.

Das ist richtig. Der offensichtliche Vorteil ist, dass du weniger 
Dateien brauchst. Mir fallen dazu folgende Nachteile ein:

1) Um die Klassen einer Bibliothek in eigenen Programmen nutzen zu 
können, brauchst du zwangsläufig deren Quelltext. Da aber nicht alle 
Anbieter auf Open-Source stehen, gibt es in Java die Interface, welche 
prinzipiell den *.h Dateien von C/C++ entsprechen.

Der Hersteller kann in Form eines Interface die Signaturen der 
Funktionen beschreiben. Der eigentliche Code wird in compilierter Form 
(einzelne *.class Dateien oder *.jar Packete) geliefert.

2) Wenn du in einer Java Quelltext-Datei irgend etwas änderst, muss der 
Compiler alle anderen Java Quelltexte neu compilieren, die davon 
abhängen. Dies führt zu unnötig aufwändigen Compiliervorgängen, denn oft 
ändert man nur den Inhalt einer Funktion, nicht deren Signatur. Diesen 
Nachteil kann man durch den Einsatz von Interfacen umgehen.

Bei Mikrocontroller Programmen ist es nicht mehr so wichtig, ob jedesmal 
alle Quelltexte compiliert werden, oder nur wenige ausgewählte Dateien. 
Denn unsere Desktop Computer sind sehr schnell geworden und µC Programme 
sind meistens eher klein.

In den 90er Jahren sah das noch ganz anders aus, da benötigte der 
Compiler für jede einzelne Datei sehr viel mehr Zeit. Jede unnötige 
Datei war da ein Ärgernis.

Deswegen hat man Build Tools wie Ant (Java) und Make (C/C++) erfunden. 
Diese Programme finden anhand der Zeitstempel heraus, welche Dateien 
verändert wurden und compilieren nur die Dateien neu, die davon 
abhängen. Solange ich in C keine Header Datei ändere, müssen nur die C 
Dateien neu compiliert werden, die ich verändere. Das konnte damals eine 
Menge Zeit einsparen.

Heute kannst du den Effekt am Linux Kerne nachvollziehen. Compiliere mal 
einen kompletten Linux Kernel. Das dauert vielleicht eine Stunde. Dann 
editiere eine einzige *.c Datei und compiliere ihn nochmal. Dieses mal 
dauert es nur Sekunden, denn es wird nur diese eine *.c Datei neu 
compiliert.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Heute kannst du den Effekt am Linux Kerne nachvollziehen. Compiliere mal
> einen kompletten Linux Kernel. Das dauert vielleicht eine Stunde. Dann
> editiere eine einzige *.c Datei und compiliere ihn nochmal. Dieses mal
> dauert es nur Sekunden, denn es wird nur diese eine *.c Datei neu
> compiliert.

Und jetzt stelle man sich mal vor, der gesamte Kernel bestünde aus einer 
einzelnen C-Datei und sonst nur aus Headern. Es würde dann nicht nur 
genauso lang wie beim ersten mal dauern, sondern vermutlich auch 50 GB 
RAM zum Compilieren brauchen, weil der Compiler dann den gesamten Code 
an einem Stück parsen und verarbeiten muss.

Autor: Cornelius (Gast)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Irgendwo hatte ich mal gelesen, dass es modern sei, nur mit *.h Files zu 
programmieren.

Stefanus F. (stefanus) schrieb:
>Stell Dir vor, du hast eine hardware.h mit der Funktion led_an(). Nun
>bindest du diese Header Datei in zwei c oder cpp Dateien ein. Dann
>existiert die Funktion doppelt, und das darf nicht sein.

Dagegen gibt es in den Header-Files die bekannte #ifndef-Klammer:
#ifndef __LED__
#define __LED__

class Led
{
  boolean ledState;
  unsigned char ledPin;

  public:
    Led(unsigned char _ledPin)
    {
      ledState=false;
      ledPin=_ledPin;
      pinMode(ledPin,OUTPUT);
    }

    void toogle()
    {
      ledState=!ledState;
      digitalWrite(ledPin,ledState);
    }
};

#endif // __LED__

Eigentlich ist eine Klasse ja eine Definition wie "struct" in C und 
diese finden sich üblicherweise in den Header-Files. Die Instantiierung 
von Objekten ( also das Anlegen von Variablen in C ) findet dann in den 
*.cpp Dateien statt.

Es ist also die Frage, ob es Beispielprojekte gibt, die versuchen auf 
*.cpp -Files zu verzichten.

In der Mozzi-Library wird fast alles mit Header-Files gemacht:
https://github.com/sensorium/Mozzi

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Dagegen gibt es in den Header-Files die bekannte #ifndef-Klammer:

Die aber überhaupt nicht gegen dieses Problem hilft, weil Makros nicht 
über einzelne Compiler Läufe erhalten bleiben. Wenn man wie gezeigt alle 
Funktionen direkt in der Klasse definiert, sind sie automatisch Inline. 
Dadurch hat man am Ende eine riesige Code Duplikation. Wenn man sie 
außerhalb der Klasse im Header definiert, bekommt man vom Linker 
multiple Definition Errors. Man sollte längere Funktionen also auf jeden 
Fall in eigene .cpp Dateien packen, kürzere kann man Inline machen.
Übrigens ist __ LED __ ein verbotener Bezeichner, da er mit zwei 
Unterstrichen anfängt. Der bool Typ heißt bool in C++, nicht boolean.
Der Grund für separate Header Dateien kommt letzendlich aus der Frühzeit 
der Compiler in den 80ern, als die Computer nicht genug Leistung hatten 
um ein komplettes Programm gleichzeitig zu betrachten. Das wurde in 
nahezu allen moderneren Sprachen anders gemacht, diese wären aber auf 
damaligen Rechnern nicht nutzbar. Leider hat sich noch keiner wirklich 
die Mühe gemacht, separate Header Files abzuschaffen...

Cornelius schrieb:
> In der Mozzi-Library wird fast alles mit Header-Files gemacht:

Weil das Templates sind und es da nicht anders geht. Hier muss man drauf 
hoffen dass der Optimizer Duplikationen entfernt.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Irgendwo hatte ich mal gelesen, dass es modern sei,
> mit *.h Files zu programmieren.

Vergiss das besser wieder ganz schnell.

>>Stell Dir vor, du hast eine hardware.h mit der Funktion led_an(). Nun
>>bindest du diese Header Datei in zwei c oder cpp Dateien ein. Dann
>>existiert die Funktion doppelt, und das darf nicht sein.
> Dagegen gibt es in den Header-Files die bekannte #ifndef-Klammer:

Das hilft nicht, denn der C Compiler compiliert jede *.c/*.cpp Datei 
einzeln. Als Ergebnis hast du dann zwei *.o Dateien und beide enthalten 
die gleichnamige Funktion led_an(). Diese beiden *.o Dateien kann der 
Linker nicht zusammenfügen, denn er verlangt eindeutige Namen. Die 
Funktion led_an() darf und muss nur in einer der beiden *.o Dateien 
existieren.

Autor: MitLeserin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(kein Arduino)
**************
In meinen uC-c++ Programmen  ist die ganze Funktionalität in den Klassen 
und damit in der *.h(pp)-Datei, Objekt-Deklarationen und int main() in 
*.c(pp). In den dabei verwendeten Bibliotheken ebenfalls.
-
IDE AtmelStudio + Editor NP++, Toolchain GCC 8.2, std=c++2a.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>In der Mozzi-Library wird fast alles mit Header-Files gemacht:
>https://github.com/sensorium/Mozzi

Die Mozzi-Library habe ich aus folgenden Gründen als Beispiel gewählt:

- ist in C++ geschrieben
- Läuft auf Atmegas
- Sound Erzeugung ist extrem zeitkritisch
- Atmegas habe wenig Speicher
- 95% des Code sind *.h files
- die Library ist extrem erfolgreich

Mann kann also viel für die Verwendung von C++ auf kleinen MC-Systemen 
lernen.

>Cornelius schrieb:
>> In der Mozzi-Library wird fast alles mit Header-Files gemacht:
Dr.Sommer schrieb:
>Weil das Templates sind und es da nicht anders geht. Hier muss man drauf
>hoffen dass der Optimizer Duplikationen entfernt.

Das habe ich übersehen. Danke für den Hinweis. Die Frage ist: wie wird 
die Verdoppelung von Code bei der Verwendung von Templates verhindert?

>Der bool Typ heißt bool in C++, nicht boolean.
In der Arduino API gibt's boolean. Das ist aus historischen Gründen der 
Kompatibilität zu Java geschuldet.

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Auteilung in Schnittstellen Deklaration und Implementierung wird bei 
C/C++ auch in der Kombination Header + kompilierte Module (Objectfiles, 
Archivfiles oder DLL) genutzt. Damit muss man eine Implementierung nicht 
veröffentlichen. Das ist zu open source Zeiten zwar aus der Mode 
gekommen, gibt es aber immer noch.
Für Klassen haben gute Editoren auch Unterstützung, bei Eclipse schreibe 
ich erstmal die Klassendefinition in den Header, Eclipse übernimmt dann 
den stupiden Teil und generiert die Methodenrümpfe in der .cpp Datei. Da 
sehe ich keinen Grund darin an dem System zu rütteln oder das wie bei 
Arduino durch .ino um zusätzliche Regeln zu erweitern.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier der erste Versuch für ein Led-Array. Durch die Trennung von Header 
und Methoden sieht das Ganze jetzt ziemlich länglich aus.
class Led
{
  boolean ledState;
  unsigned char ledPin;

  public:
    Led();
    void setPin(unsigned char);
    void toogle();
};

Led::Led()
{
  ledState=false;
  ledPin=0;
}

void Led::setPin(unsigned char _ledPin)
{
  ledState=false;
  ledPin=_ledPin;
  pinMode(ledPin,OUTPUT);
}

void Led::toogle()
{
  ledState=!ledState;
  digitalWrite(ledPin,ledState);
}

Led leds[10];

void setup()
{
  leds[0].setPin(LED_BUILTIN);
}

void loop()
{
  leds[0].toogle();
  delay(100);
}

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Durch die Trennung von Header und Methoden

Da ist keine Trennung von "Header und Methoden" zu sehen. Man könnte die 
Klassendeklaration in eine Headerdatei (*.h oder *.hpp, je nach 
Geschmack) auslagern, und die Definitionen der Memberfunktionen wiederum 
in einer Sourcedatei (*.cpp) unterbringen.

Dazu müsste man unterhalb der ersten schließenden geschweiften Klammer 
eine neue Datei anfangen.

Im übrigen:
Der Begriff "Methode" ist eher im Java-Umfeld gebräuclich.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rufus Τ. F. schrieb:
> Im übrigen:
> Der Begriff "Methode" ist eher im Java-Umfeld gebräuclich.

Es ist ein Begriff aus der allgemeinen objektorientierten 
Programmierung. Im C++-Umfeld ist er aber nicht genau definiert, und 
jeder versteht was anderes darunter. Ich hab in der Praxis schon die 
Verwendung des Begriffs in mindestens drei verschiedenen Bedeutungen 
erlebt. Daher halte ich es für besser, ihn in C++ nicht zu verwenden.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Begriffe in C++ sind ohnehin ziemlich gaga.

Ein Objekt soll gemäß Lehrbuch ein Ding mit seinen Eigenschaften (Daten) 
und Funktionen repräsentieren.

In C++ verwendet man den Begriff Objekt aber für die Definition. 
Speicherplatz wird durch die Deklaration belegt und das tatsächliche 
Objekt mit Daten heißt dann Objekt-Instanz.

An anderen Stellen werden die Begriffe "Definition" und "Deklaration" 
synonym verwendet oder genau umgekehrt als oben Beschrieben. Funktionen 
heißen plötzlich Methoden und was "statisch" bedeutet ist nicht einmal 
innerhalb der Sprache einheitlich.

Da soll man als Programmierer, der in zahlreichen Programmiersprachen 
entwickeln muss, nicht bekloppt werden?

Mein Tipp: Legt die Begriffe nicht in die Waagschale, solange erkennbar 
ist, was der Schreiber meint. Was heute gilt, kann morgen schon obsolet 
sein.

: Bearbeitet durch User
Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vermutlich kommt ein Teil der Verwirrung auch aus der (üblicherweise 
sehr schlechten) Übersetzung ins Deutsche.

Das soll jetzt nicht bedeuten, daß englischsprachige Literatur 
prinzipiell überlegen und fehlerfrei wäre, aber in der Übersetzung kann 
halt viel verloren gehen.

Auch sind deutschprachige Akademiker nach wie vor oft der Ansicht, 
einfache Sachverhalte so komplex wie nur möglich darstellen zu müssen, 
andernfalls wäre das ja nicht akademisch.

Und je nach Lebensalter des Informatik-Akademikers werden auch noch die 
deutschsprachigen Koteteletten-und-Hornbrille-Fachbegriffe der 60er 
Jahre exhumiert; noch gar nicht lange her, und jemand verwendete so ein 
Fossil, der "Kellerspeicher" war es.

Obendrein gibt es auch tatsächlich schlechte deutschsprachige Literatur, 
wie z.B. den "Schellong" ("Moderne C-Programmierung").

Gut geschriebene, fachlich korrekte Informatik-Literatur ist jedenfalls 
sehr, sehr rar.

Autor: Cornelius (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Ok, so weit zu *.h und *.cpp

Jetzt mal was Praktisches. Ein sehr einfaches Programm, aber der 
Compiler wirft 2 Fehler:

error: variable or field 'test' declared void
error: 'Base' was not declared in this scope

class Base
{
public:
  void show()
  {
    Serial.println("Base");
  }
};

class Derived: public Base
{
public:
  void show()
  {
    Serial.println("Derived");
  }
};

void test(Base a)
{
  Serial.print("test:");
  a.show();
}


void setup()
{
  Serial.begin(115200);

  Derived d;
  test(d);
}

void loop()
{

}

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann ich nicht nachvollziehen, bei mir compiliert es ohne Fehlermeldung:
/home/daten/arduino-1.8.7/arduino-builder -dump-prefs -logger=machine -hardware /home/daten/arduino-1.8.7/hardware -hardware /home/daten/arduino-1.8.7/portable/packages -tools /home/daten/arduino-1.8.7/tools-builder -tools /home/daten/arduino-1.8.7/hardware/tools/avr -tools /home/daten/arduino-1.8.7/portable/packages -built-in-libraries /home/daten/arduino-1.8.7/libraries -libraries /home/daten/arduino-1.8.7/portable/sketchbook/libraries -fqbn=arduino:avr:nano:cpu=atmega328 -ide-version=10807 -build-path /tmp/arduino_build_280773 -warnings=all -build-cache /tmp/arduino_cache_246701 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avrdude.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avrdude-6.3.0-arduino14.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avr-gcc.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avr-gcc-5.4.0-atmel3.6.1-arduino2.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.arduinoOTA.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.arduinoOTA-1.2.1.path=/home/daten/arduino-1.8.7/hardware/tools/avr -verbose /home/stefan/Downloads/sketch_nov25b/sketch_nov25b.ino
/home/daten/arduino-1.8.7/arduino-builder -compile -logger=machine -hardware /home/daten/arduino-1.8.7/hardware -hardware /home/daten/arduino-1.8.7/portable/packages -tools /home/daten/arduino-1.8.7/tools-builder -tools /home/daten/arduino-1.8.7/hardware/tools/avr -tools /home/daten/arduino-1.8.7/portable/packages -built-in-libraries /home/daten/arduino-1.8.7/libraries -libraries /home/daten/arduino-1.8.7/portable/sketchbook/libraries -fqbn=arduino:avr:nano:cpu=atmega328 -ide-version=10807 -build-path /tmp/arduino_build_280773 -warnings=all -build-cache /tmp/arduino_cache_246701 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avrdude.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avrdude-6.3.0-arduino14.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avr-gcc.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.avr-gcc-5.4.0-atmel3.6.1-arduino2.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.arduinoOTA.path=/home/daten/arduino-1.8.7/hardware/tools/avr -prefs=runtime.tools.arduinoOTA-1.2.1.path=/home/daten/arduino-1.8.7/hardware/tools/avr -verbose /home/stefan/Downloads/sketch_nov25b/sketch_nov25b.ino
Using board 'nano' from platform in folder: /home/daten/arduino-1.8.7/hardware/arduino/avr
Using core 'arduino' from platform in folder: /home/daten/arduino-1.8.7/hardware/arduino/avr
Detecting libraries used...
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10807 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/daten/arduino-1.8.7/hardware/arduino/avr/cores/arduino -I/home/daten/arduino-1.8.7/hardware/arduino/avr/variants/eightanaloginputs /tmp/arduino_build_280773/sketch/sketch_nov25b.ino.cpp -o /dev/null
Generating function prototypes...
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10807 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/daten/arduino-1.8.7/hardware/arduino/avr/cores/arduino -I/home/daten/arduino-1.8.7/hardware/arduino/avr/variants/eightanaloginputs /tmp/arduino_build_280773/sketch/sketch_nov25b.ino.cpp -o /tmp/arduino_build_280773/preproc/ctags_target_for_gcc_minus_e.cpp
/home/daten/arduino-1.8.7/tools-builder/ctags/5.8-arduino11/ctags -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives /tmp/arduino_build_280773/preproc/ctags_target_for_gcc_minus_e.cpp
Sketch wird kompiliert...
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-g++ -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10807 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/daten/arduino-1.8.7/hardware/arduino/avr/cores/arduino -I/home/daten/arduino-1.8.7/hardware/arduino/avr/variants/eightanaloginputs /tmp/arduino_build_280773/sketch/sketch_nov25b.ino.cpp -o /tmp/arduino_build_280773/sketch/sketch_nov25b.ino.cpp.o
Compiling libraries...
Compiling core...
Using precompiled core: /tmp/arduino_cache_246701/core/core_arduino_avr_nano_cpu_atmega328_fe15b8034bc287c8db30434e4d6d501e.a
Linking everything together...
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-gcc -Wall -Wextra -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=atmega328p -o /tmp/arduino_build_280773/sketch_nov25b.ino.elf /tmp/arduino_build_280773/sketch/sketch_nov25b.ino.cpp.o /tmp/arduino_build_280773/../arduino_cache_246701/core/core_arduino_avr_nano_cpu_atmega328_fe15b8034bc287c8db30434e4d6d501e.a -L/tmp/arduino_build_280773 -lm
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 /tmp/arduino_build_280773/sketch_nov25b.ino.elf /tmp/arduino_build_280773/sketch_nov25b.ino.eep
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-objcopy -O ihex -R .eeprom /tmp/arduino_build_280773/sketch_nov25b.ino.elf /tmp/arduino_build_280773/sketch_nov25b.ino.hex
/home/daten/arduino-1.8.7/hardware/tools/avr/bin/avr-size -A /tmp/arduino_build_280773/sketch_nov25b.ino.elf
Der Sketch verwendet 1516 Bytes (4%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes.
Globale Variablen verwenden 198 Bytes (9%) des dynamischen Speichers, 1850 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Ich habe die Compiler-Warnungen auf die Stufe "Alle" gestellt.

: Bearbeitet durch User
Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Jetzt mal was Praktisches. Ein sehr einfaches Programm, aber der
> Compiler wirft 2 Fehler:

Und das steht so, wie Du es zwischen die [ c ] / [ /c ]-Tags gesetzt 
hast, auch in einer Datei?

Autor: Cornelius (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Ja, in der *.ino.
Allerdings compiliere ich für den ESP8266.
esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106 
-elf-g++"  ...
../sloeber.ino.cpp:9:11: error: variable or field 'test' declared void

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Anscheinend erkennt er aus irgendeinem Grund die Definition der Klasse 
Base nicht. Das würde beide Fehler erklären.
Der Code sieht aber in der Hinsicht soweit richtig aus (auch wenn er 
wohl nicht das machen wird, was du eigentlich willst).

: Bearbeitet durch User
Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann ich immer noch nicht nachvollziehen. Auch für den ESP8266 
compiliert es bei mir ohne Fehlermeldung:
/home/daten/arduino-1.8.7/arduino-builder -dump-prefs -logger=machine -hardware /home/daten/arduino-1.8.7/hardware -hardware /home/daten/arduino-1.8.7/portable/packages -tools /home/daten/arduino-1.8.7/tools-builder -tools /home/daten/arduino-1.8.7/hardware/tools/avr -tools /home/daten/arduino-1.8.7/portable/packages -built-in-libraries /home/daten/arduino-1.8.7/libraries -libraries /home/daten/arduino-1.8.7/portable/sketchbook/libraries -fqbn=esp8266:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=512K64,ResetMethod=ck,Debug=Disabled,DebugLevel=None____ -ide-version=10807 -build-path /tmp/arduino_build_289241 -warnings=all -build-cache /tmp/arduino_cache_768564 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.esptool.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/esptool/0.4.9 -prefs=runtime.tools.esptool-0.4.9.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/esptool/0.4.9 -prefs=runtime.tools.mkspiffs.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/mkspiffs/0.1.2 -prefs=runtime.tools.mkspiffs-0.1.2.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/mkspiffs/0.1.2 -prefs=runtime.tools.xtensa-lx106-elf-gcc.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2 -prefs=runtime.tools.xtensa-lx106-elf-gcc-1.20.0-26-gb404fb9-2.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2 -verbose /home/stefan/Downloads/sketch_nov25b/sketch_nov25b.ino
/home/daten/arduino-1.8.7/arduino-builder -compile -logger=machine -hardware /home/daten/arduino-1.8.7/hardware -hardware /home/daten/arduino-1.8.7/portable/packages -tools /home/daten/arduino-1.8.7/tools-builder -tools /home/daten/arduino-1.8.7/hardware/tools/avr -tools /home/daten/arduino-1.8.7/portable/packages -built-in-libraries /home/daten/arduino-1.8.7/libraries -libraries /home/daten/arduino-1.8.7/portable/sketchbook/libraries -fqbn=esp8266:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=512K64,ResetMethod=ck,Debug=Disabled,DebugLevel=None____ -ide-version=10807 -build-path /tmp/arduino_build_289241 -warnings=all -build-cache /tmp/arduino_cache_768564 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.esptool.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/esptool/0.4.9 -prefs=runtime.tools.esptool-0.4.9.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/esptool/0.4.9 -prefs=runtime.tools.mkspiffs.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/mkspiffs/0.1.2 -prefs=runtime.tools.mkspiffs-0.1.2.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/mkspiffs/0.1.2 -prefs=runtime.tools.xtensa-lx106-elf-gcc.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2 -prefs=runtime.tools.xtensa-lx106-elf-gcc-1.20.0-26-gb404fb9-2.path=/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2 -verbose /home/stefan/Downloads/sketch_nov25b/sketch_nov25b.ino
Using board 'generic' from platform in folder: /home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0
Using core 'esp8266' from platform in folder: /home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0
Detecting libraries used...
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-g++ -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/include -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/lwip/include -I/tmp/arduino_build_289241/core -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -ffunction-sections -fdata-sections -w -x c++ -E -CC -DF_CPU=80000000L -DLWIP_OPEN_SRC -DARDUINO=10807 -DARDUINO_ESP8266_ESP01 -DARDUINO_ARCH_ESP8266 "-DARDUINO_BOARD=\"ESP8266_ESP01\"" -DESP8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/variants/generic /tmp/arduino_build_289241/sketch/sketch_nov25b.ino.cpp -o /dev/null
Generating function prototypes...
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-g++ -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/include -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/lwip/include -I/tmp/arduino_build_289241/core -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -ffunction-sections -fdata-sections -w -x c++ -E -CC -DF_CPU=80000000L -DLWIP_OPEN_SRC -DARDUINO=10807 -DARDUINO_ESP8266_ESP01 -DARDUINO_ARCH_ESP8266 "-DARDUINO_BOARD=\"ESP8266_ESP01\"" -DESP8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/variants/generic /tmp/arduino_build_289241/sketch/sketch_nov25b.ino.cpp -o /tmp/arduino_build_289241/preproc/ctags_target_for_gcc_minus_e.cpp
/home/daten/arduino-1.8.7/tools-builder/ctags/5.8-arduino11/ctags -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives /tmp/arduino_build_289241/preproc/ctags_target_for_gcc_minus_e.cpp
Sketch wird kompiliert...
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-g++ -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/include -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/lwip/include -I/tmp/arduino_build_289241/core -c -Wall -Wextra -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections -DF_CPU=80000000L -DLWIP_OPEN_SRC -DARDUINO=10807 -DARDUINO_ESP8266_ESP01 -DARDUINO_ARCH_ESP8266 "-DARDUINO_BOARD=\"ESP8266_ESP01\"" -DESP8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266 -I/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/variants/generic /tmp/arduino_build_289241/sketch/sketch_nov25b.ino.cpp -o /tmp/arduino_build_289241/sketch/sketch_nov25b.ino.cpp.o
Compiling libraries...
Compiling core...
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/cont.S.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring_analog.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_noniso.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/cont_util.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_si2c.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring_shift.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring_digital.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_phy.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_i2s.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring_pwm.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring_pulse.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs/spiffs_hydrogen.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs/spiffs_check.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/heap.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_flash_utils.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_wiring.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/libc_replacements.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/libb64/cdecode.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/time.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/libb64/cencode.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs/spiffs_gc.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs/spiffs_nucleus.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs/spiffs_cache.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_timer.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/umm_malloc/umm_malloc.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_postmortem.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_eboot_command.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/uart.c.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Esp.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/FS.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/cbuf.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/pgmspace.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/HardwareSerial.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs_hal.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/WMath.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/debug.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/WString.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/base64.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/core_esp8266_main.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/abi.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/spiffs_api.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Schedule.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/IPAddress.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/StreamString.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Tone.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Print.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Updater.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/MD5Builder.cpp.o
Zuvor kompilierte Datei wird verwendet: /tmp/arduino_build_289241/core/Stream.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Esp.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/FS.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/HardwareSerial.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/IPAddress.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/MD5Builder.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Print.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Schedule.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Stream.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/StreamString.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Tone.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/Updater.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/WMath.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/WString.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/abi.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/base64.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/cbuf.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/cont.S.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/cont_util.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_eboot_command.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_flash_utils.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_i2s.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_main.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_noniso.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_phy.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_postmortem.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_si2c.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_timer.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring_analog.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring_digital.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring_pulse.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring_pwm.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/core_esp8266_wiring_shift.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/debug.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/heap.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/libb64/cdecode.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/libb64/cencode.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/libc_replacements.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/pgmspace.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs/spiffs_cache.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs/spiffs_check.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs/spiffs_gc.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs/spiffs_hydrogen.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs/spiffs_nucleus.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs_api.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/spiffs_hal.cpp.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/time.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/uart.c.o
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-ar cru /tmp/arduino_build_289241/arduino.ar /tmp/arduino_build_289241/core/umm_malloc/umm_malloc.c.o
Unable to cache built core, please tell esp8266 maintainers to follow http://goo. gl/QdCUjo
Linking everything together...
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-gcc -g -Wall -Wextra -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -L/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/lib -L/home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/tools/sdk/ld -Teagle.flash.512k64.ld -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy -o /tmp/arduino_build_289241/sketch_nov25b.ino.elf -Wl,--start-group /tmp/arduino_build_289241/sketch/sketch_nov25b.ino.cpp.o /tmp/arduino_build_289241/arduino.ar -lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 -llwip_gcc -lstdc++ -Wl,--end-group -L/tmp/arduino_build_289241
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/esptool/0.4.9/esptool -eo /home/daten/arduino-1.8.7/portable/packages/esp8266/hardware/esp8266/2.3.0/bootloaders/eboot/eboot.elf -bo /tmp/arduino_build_289241/sketch_nov25b.ino.bin -bm dio -bf 40 -bz 512K -bs .text -bp 4096 -ec -eo /tmp/arduino_build_289241/sketch_nov25b.ino.elf -bs .irom0.text -bs .text -bs .data -bs .rodata -bc -ec
/home/daten/arduino-1.8.7/portable/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-size -A /tmp/arduino_build_289241/sketch_nov25b.ino.elf
Der Sketch verwendet 224177 Bytes (51%) des Programmspeicherplatzes. Das Maximum sind 434160 Bytes.
Globale Variablen verwenden 31664 Bytes (38%) des dynamischen Speichers, 50256 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke.
Ich glaube der Fehler liegt ganz wo anders: Ich verwende den ESP8266 und 
habe das Ganze mit der Eclipse-Sloeber-IDE compiliert.
Wenn ich das in der Arduino-IDE mache, verschwindet der Fehler. Ich 
glaub's nicht ...

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, ich hätte den log-Auszug verkürzen sollen.

Schön zu lesen, dass du der Problemursache näher gekommen bist.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Schön zu lesen, dass du der Problemursache näher gekommen bist.
Ja, mühsam ernährt sich das Eichhörnchen. Nicht dass ich nur mit C++ 
kämpfen muss, jetzt hat mir auch noch die Sloeber-IDE dazwischen 
gefunkt.
Sie hat wohl ein Problem damit, wenn Klassen und Funktionen im *.ino 
File gemischt sind.
Jetzt bin ich wieder bei der Arduino-IDE.

Hier ein neues Programm. Ich will verschiedene Objekte, die alle von 
einer Basisklasse abstammen in eine Liste eintragen und deren 
Eigenschaften drucken.

Aus irgend welchen Gründen wird aber nur die Basisklasse gedruckt:
class Base
{
  public:
    void show()
    {
      Serial.println("Base");
    }
};


class Derived: public Base
{
  public:
    void show()
    {
      Serial.println("Derived");
    }
};

class Viewer
{
    std::vector<Base> objects;
    
  public:
    void add(Base a)
    {
      objects.push_back(a);
    }

    void show()
    {
      for (uint n = 0; n < objects.size(); n++)
      {
        objects[n].show();
      }
      Serial.println("");
    }
};


Viewer v;

void setup()
{
  Serial.begin(115200);

  Derived d;
  v.add(d);

  Base b;
  v.add(b);
}

void loop()
{
  v.show();
  delay(1000);
}


Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du Funktionen einer Basisklasse überschrieben willst, müssen sie 
als "virtual" deklariert werden.
https://de.wikibooks.org/wiki/C%2B%2B-Programmierung:_Polymorphie

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Aus irgend welchen Gründen wird aber nur die Basisklasse gedruckt:

Und da kommen wir dann zu dem Teil:

Rolf M. schrieb:
> (auch wenn er wohl nicht das machen wird, was du eigentlich willst)

Cornelius schrieb:
>     std::vector<Base> objects;

Hier definierst du einen Vektor aus Instanzen der Klasse Base. Der kann 
genau das speichern. Derived kann er nicht speichern.

>     void add(Base a)

Und hier definierst du eine Funktion, die ein Base als Parameter per 
Kopie übergeben bekommt. Auch hier: Die Klasse kann ausschließlich 
Instanzen von Base annehmen und von nichts anderem.

Cornelius schrieb:
> Derived d;
>   v.add(d);

Was hier passiert, nennt sich "Slicing". ( 
https://stackoverflow.com/questions/274626/what-is-object-slicing ). Da 
v.add einen Base per Kopie haben will, bekommt es auch nur eine Kopie 
des Base-Teils des Objekts. Der Derived-Teil wird abgeschnitten.
Das sind aber recht grundlegende Dinge in C++. Mach dich darüber schlau 
wie Polymorphie in C++ funktioniert und schau dir an, was eine Referenz 
ist.
Wobei du hier eher Zeiger brauchst, denn einen std::vector<Base> kannst 
du dafür nicht nehmen. Du brauchst einen std::vector<Base*>.

Stefanus F. schrieb:
> Wenn du Funktionen einer Basisklasse überschrieben willst, müssen sie
> als "virtual" deklariert werden.

Ja, das kommt noch dazu - hatte ich übersehen.

: Bearbeitet durch User
Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, jetzt wird Derived und Base gedruckt, aber mit abschließendem 
Absturz ....
class Base
{
  public:
    virtual void show()
    {
      Serial.println("Base");
    }
};

class Derived: public Base
{
  public:
    void show()
    {
      Serial.println("Derived");
    }
};

class Viewer
{
    std::vector<Base*> objects;
    
  public:
    void add(Base *a)
    {
      objects.push_back(a);
    }

    void show()
    {
      for (uint n = 0; n < objects.size(); n++)
      {
        objects[n]->show();
      }
      Serial.println("");
    }
};

Viewer v;

void setup()
{
  Serial.begin(115200);

  Derived d;
  v.add(&d);

  Base b;
  v.add(&b);
}

void loop()
{
  v.show();
  delay(1000);
}


Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das liegt daran, dass d ein lokales Objekt in setup() ist. Nach Ablauf 
der Funktion existiert es nicht mehr, und man darf nicht mehr darauf 
zugreifen.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK, damit funktioniert's.

Aber so richtig gefallen tut es mir nicht, weil ich gerne hätte, dass 
Objekte nah am Ort der Verwendung definiert werden.

Wie ist es damit?:


Viewer v;

void setup()
{
  Serial.begin(115200);

  v.add(new Derived());
  v.add(new Base());
}

void loop()
{
  v.show();
  delay(1000);
}


Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> OK, damit funktioniert's.
>
> Aber so richtig gefallen tut es mir nicht, weil ich gerne hätte, dass
> Objekte nah am Ort der Verwendung definiert werden.

Naja, wenn sie in setup definiert sind und in loop verwendet werden, ist 
das in dem Sinne nicht nah.

> Wie ist es damit?:

Ja, das wäre der klassische Weg, wie man es auf dem PC macht. Du musst 
dann natürlich daran denken, dass du die Objekte wieder explizit mit 
delete löschen musst, wenn du sie nicht mehr brauchst. Wenn sie aber eh 
"für immmer" existieren, könntest du sie alternativ in setup als static 
definieren. Das ist allerdings auch nicht gerade elegant, aber man spart 
sich die dynamische Allokation.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit new müsste es gehen, weil die Objekte dann auf dem Heap wohnen.

By the way: Die Grundlagen von C/C++ lernt man auf dem PC viel leichter.

Lade Dir QT Creator herunter, das verwendet auch den GCC Compiler (in 
der Variante für deinen PC). Mit dem Debugger kannst du gut 
nachvollziehen, was passiert. Der eingebaute Lint Checker weist schon 
während der Eingabe auf mögliche Fehler hin.

Wenn Dir QT Creator gefällt, kannst du den avr-gcc und auch den arm-gcc 
für deine Arduino Projekte hinzufügen und auch diese damit editieren. 
Ich nutze QT Creator als "externer Editor" zusammen mit der Arduino IDE 
zum Compilieren und Flashen.

: Bearbeitet durch User
Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Moment bin ich gerade auf den PC umgestiegen und nutze Eclipse.
Ich will die Klassen in der virtuellen PC-Umgebung so entwickeln, dass 
ich sie 1:1 auf den MC übertragen kann.

Und schon entsteht das erste Problem.

Ich will "String" verwenden:
https://www.arduino.cc/reference/en/language/variables/data-types/string/
https://github.com/esp8266/Arduino/blob/master/cores/esp8266/WString.h

Das gibt's aber wohl leider nicht auf dem PC. Also versuche ich WString 
auf den PC zu übertragen, was aber im Moment ziemliche Schwierigkeiten 
macht.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Arduino-String-Klasse ist für Mikrocontroller optimiert. Auf dem PC 
verwendet man std::string. uC und PC Programme lassen sich nicht 1:1 
übertragen, besonders wenn es um AVR mit den diversen Sonderlocken 
(PROGMEM und so) geht.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Die Arduino-String-Klasse ist für Mikrocontroller optimiert. Auf dem PC
>verwendet man std::string.

Die habe ich schon probiert, passt aber leider ziemlich schlecht. Wenn 
man sich bemüht, sollte man die Kompatibilität schaffen.

>(PROGMEM und so)

Auf dem ESP:
https://github.com/esp8266/Arduino/blob/master/cores/esp8266/pgmspace.h

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Die habe ich schon probiert, passt aber leider ziemlich schlecht. Wenn
> man sich bemüht, sollte man die Kompatibilität schaffen.

Dann viel Spaß - dafür ist Arduino halt gar nicht gemacht.

Du kannst Standard C++ programmieren, dann funktionieren deine Programme 
garantiert auf allen Standard-konformen Compilern, und damit auf allen 
gängigen PC-Betriebssystemen. Leider sind Embedded-Targets, insbesondere 
für AVR, nicht standardkonform.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Du kannst Standard C++ programmieren

Sicher? Ich glaube die std:: Klassen stehen wiederum auf Arduino nicht 
zur Verfügung.

Ehrlich gesagt meide ich die String Klasse von Arduino ohnehin wie der 
teufel das Weihwasser, weil sie dazu verleitet, den Heap zu 
fragmentieren. Wenn ich ständig nachdenken muss, was unter der Haube der 
Klasse passiert, kann ich gleich char[] und die Funktionen der 
<string.h> verwenden.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Sicher? Ich glaube die std:: Klassen stehen wiederum auf Arduino nicht
> zur Verfügung.

Sag ich doch.

Stefanus F. schrieb:
> weil sie dazu verleitet, den Heap zu
> fragmentieren.

Ich vermeide die Nutzung des Heaps gleich ganz... Mit ein bisschen 
Planung braucht man den nicht, und spart sich manche böse Überraschung.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Langsam glaube ich, der Espressiv-Compiler für den ESP8266 hat einen 
Schuss:
void setup() 
{
  Serial.begin(115200);
}

void loop() 
{
  uint32_t color=0x102030UL;
  
  uint8_t r=((uint32_t)color>>24) & 0xFF;
  uint8_t g=((uint32_t)color>> 8) & 0xFF;
  uint8_t b=((uint32_t)color    ) & 0xFF;
  
  Serial.print(" r:");Serial.print(r);
  Serial.print(" g:");Serial.print(g);
  Serial.print(" b:");Serial.print(b);
  Serial.println();
  
  delay(1000);
}

Ergebnis:
 r:0 g:32 b:48

Aber r sollte 16 sein !!

: Bearbeitet durch Moderator
Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Aber r sollte 16 sein !!

Dann solltest Du um 16 Bits und nicht um 24 Bits schieben, schließlich 
sieht Deine Konstante so aus:

0x00RRGGBB

Mit dem Schieben um 24 Bits erhältst Du folgerichtig die 0.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uhh ... ich war blind ... Danke !

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Viewer v;
>
>void setup()
>{
>  Serial.begin(115200);
>
>  v.add(new Derived());
>  v.add(new Base());
>}
>void loop()
>{
>  v.show();
>  delay(1000);
>}

Kann ich mit
objects.erase();

Die Objekte löschen und den Speicher freigeben?

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Die Objekte löschen und den Speicher freigeben?

Dazu dient die delete Anweisung.

Object* o=new Object();
o->doSomething();
delete o;

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Kann ich mitobjects.erase();
>
> Die Objekte löschen und den Speicher freigeben?

erase() erwartet einen oder zwei Parameter, die angeben, welche Elemente 
gelöscht werden sollen. Wenn du den ganzen Container leeren willst, 
musst du clear() verwenden.
Gelöscht werden dann aber nur die Zeiger, nicht die Objekte, die du mit 
new angelegt hast.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
erase() entfernt die Objekte aus dem Vektor und löscht sie auch.

Wenn man sie allerdings - wie im Code oben - auf dem Stack angelegt hat, 
ist das keine gute Idee.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> erase() entfernt die Objekte aus dem Vektor und löscht sie auch.

Richtig. In diesem Fall die Pointer, die im Vektor gespeichert sind.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus, Rolf: Eure Aussage widersprechen sich. Was stimmt jetzt?

Mir scheint das Thema Speicherverwaltung recht schwierig. In Java gibt 
es ja den "Garbage Collector", der immer wieder mal aufräumt.

Wer räumt in C++ auf, wenn abwechselnd Objekte im Speicher liegen und 
jedes zweite gelöscht wird. Dann entstehen ja Löcher und man müsste 
quasi "defragmentieren" wie die Festplatte bei Windows früher.

Die Frage ist, welche Methoden gibt es, um das Problem zu lösen?
Kann man den Objekten, die man löschen und wieder erzeugen möchte einen 
bestimmten Speicherbereich zuweisen?

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Markus, Rolf: Eure Aussage widersprechen sich. Was stimmt jetzt?

Ein vector enthält Objekte von dem Typ, mit dem er angelegt wurde. Diese 
werden in den vector hinein kopiert. Bei einem std::vector<Base*> also 
ein Base*.

Wenn du also schreibst:
    Derived d;
    v.add(&d);

Dann wird mit &d ein Zeiger auf d gebildet, in einen Zeiger auf Base 
konvertiert, und dieser Zeiger wird dann in den Vektor kopiert. Wenn du 
ein Element (oder den ganzen Inhalt) des vectors löschst, wird der 
Pointer wieder aus dem vector entfernt. Auf d hat das aber keinerlei 
Einfluss, ganz egal, wo es gespeichert ist.

> Mir scheint das Thema Speicherverwaltung recht schwierig. In Java gibt
> es ja den "Garbage Collector", der immer wieder mal aufräumt.

Dafür gibt's da keine vernünftig funktionierenden Destruktoren, d.h. um 
alle anderen Ressourcen muss man sich von Hand kümmern.

> Wer räumt in C++ auf, wenn abwechselnd Objekte im Speicher liegen und
> jedes zweite gelöscht wird. Dann entstehen ja Löcher und man müsste quasi
> "defragmentieren" wie die Festplatte bei Windows früher.

Speicherfragmentierung gibt es, kann auf kleinen µCs auch schnell zum 
Problem werden. Das ist mit ein Grund, weshalb man dort möglichst auf 
dynamischen Speicher verzichtet.
Auf PCs ist der Speichermanager recht ausgeklügelt (und hat durch den 
großen Speicher viel mehr Möglichkeiten), um das zu vermeiden. Ein 
Garbage Collector hat tatsächlich etwas bessere Möglichkeiten, weil er 
sowieso alle Referenzen auf ein bestimmtes Objekt kennen muss und es 
daher im Speicher auch verschieben kann, solange er alle Referenzen 
darauf entsprechend anpasst. Wie weit sowas in der Praxis auch gemacht 
wird, weiß ich aber nicht.

> Die Frage ist, welche Methoden gibt es, um das Problem zu lösen?
> Kann man den Objekten, die man löschen und wieder erzeugen möchte einen
> bestimmten Speicherbereich zuweisen?

Nein, der wird automatisch vergeben.
Bedenke, dass der vector selbst auch wieder dynamischen Speicher 
braucht, denn irgendwo müssen die darin gespeicherten Elemente ja auch 
hin. Wenn du da immer wieder neue Elemente hinzufügst, muss er 
regelmäßig reallokieren, was gerade besonders zu Fragmentierung führt.

: Bearbeitet durch User
Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Wer räumt in C++ auf, wenn abwechselnd Objekte im Speicher liegen und
> jedes zweite gelöscht wird.

Niemand, das musst du notfalls selbst Implementieren. oder besser von 
vorne herein vermeiden.

Das Problem bei C ist: Man kann nicht einfach Objekte im Speicher 
verschieben, ohne dass Zeiger und Referenzen ungültig werden. Bei Java 
werden diese tatsächlich vom Garbage Collector gefunden und korrigiert, 
aber dazu muss das ganze Programm zeitweise angehalten werden. Das 
willst du auf einem Mikrocontroller sicher nicht tun.

Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Cornelius schrieb:
>> Die Frage ist, welche Methoden gibt es, um das Problem zu lösen?
>> Kann man den Objekten, die man löschen und wieder erzeugen möchte einen
>> bestimmten Speicherbereich zuweisen?
>
> Nein, der wird automatisch vergeben.
> Bedenke, dass der vector selbst auch wieder dynamischen Speicher
> braucht, denn irgendwo müssen die darin gespeicherten Elemente ja auch
> hin. Wenn du da immer wieder neue Elemente hinzufügst, muss er
> regelmäßig reallokieren, was gerade besonders zu Fragmentierung führt.

Doch, kann man. Man kann den Operator new überschreiben.
Aber wer sowas macht, der weiß auch warum.

merciless

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
man kann in eigenen Speicherverwaltungsroutinen auch mit Memory Pools 
arbeiten und so Speicherbereiche für wichtige und weniger wichtige 
Sachen aufteilen. Man sollte sich dann aber Online Diagnosemöglichkeiten 
einbauen und viel testen.

Autor: MitLeserin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Cornelius

Arduino meint Mikrokontroller mit c++.

Das was ich in diesem Thread bisher lese hat mit Mikrokontroller mit c++ 
nicht  wirklich zu tun, new, delete, heap ect. braucht man dazu nicht.
-
Die "Objekte" müssen nicht im Speicher erzeugt werden.
-
Es werden nur die Register des Mikrokontroller gelesen und beschrieben 
und dazu reichen struct oder class als typedef.
-
Falls das interessiert, dann kann der interessierte Leser seine Zeit 
z.B. in Mcucpp investieren. Die Bibliothek nutzt c++03 und hat auch 
Beispiele. Die Softwarestruktur von Mcucpp ist für verschiedene 
uC-Familien geeignet.

Wilhelm M. kann dasselbe auch mit c++2a in deutlich komplexeren 
Strukturen.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das was ich in diesem Thread bisher lese hat mit Mikrokontroller mit c++
>nicht  wirklich zu tun, new, delete, heap ect. braucht man dazu nicht.

Aber klar doch, jedes Code-Stück was Du hier siehst läuft auf dem 
ESP8266 mit der Arduino IDE.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier ein Versuch, eine Klasse zu schreiben, die sich selbst in eine 
Liste einträgt:

================ Tier.h ====================
#include "Arduino.h"
#include <vector>

class Tier
{
    String name;
    
  public:
    Tier(String _name);
    virtual std::vector<Tier*> getObjects(int get);
    void show();
    void showAll();
};

extern std::vector<Tier*> tiere;
================ Tier.cpp ====================
#include "Tier.h"

std::vector<Tier*> tiere;

Tier::Tier(String _name)
{
  name = _name;
  tiere.push_back(this);
}

std::vector<Tier*> Tier::getObjects(int get)
{
  return tiere;
}

void Tier::show()
{
  Serial.println(name);
}

void Tier::showAll()
{
  for (int n = 0; n < tiere.size(); n++) 
  {
    Serial.println(n);
    tiere[n]->show();
  }
}

================ TierTest.ino ====================
#include "Tier.h"

Tier hund("bello");
Tier katze("miau");

void setup() 
{
  Serial.begin(115200);
  delay(10);
  Serial.println("go");
}

void loop() 
{
  Serial.println("alive");
  delay(1000);
  hund.showAll();
  delay(1000);
}

Ergebnis: Die Loop zeigt, dass das Programm lebt, aber es werden keine 
Tiernamen gedruckt. Woran könnte das liegen?

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da ich keinen Fehler erkennen kann, habe ich das mal auf meinem PC (ohne 
Arduino) ausprobiert:
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Tier
{
    string name;

  public:
    Tier(string _name);
    virtual vector<Tier*> getObjects(int get);
    void show();
    void showAll();
};

vector<Tier*> tiere;

Tier::Tier(string _name)
{
  name = _name;
  tiere.push_back(this);
}

vector<Tier*> Tier::getObjects(int get)
{
  return tiere;
}

void Tier::show()
{
  cout << name << endl;
}

void Tier::showAll()
{
  for (int n = 0; n < tiere.size(); n++)
  {
    cout << n << ":";
    tiere[n]->show();
  }
}

Tier hund("bello");
Tier katze("miau");

int main()
{
    hund.showAll();
    return 0;
}
Ausgabe:
0:bello
1:miau

Läuft also. Dein Problem  muss irgendwie mit dem Arduino Framework zu 
tun haben. Programmieren übt man besser auf einem PC.

Eine Anmerkung dazu: Objekte mit virtuellen Funktionen sollten auch 
einen virtuellen Destruktor haben, selbst wenn der leer ist. Ich kann 
Dir nur empfehlen, die Qt Creator IDE als Editor zu verwenden, die weist 
auf solche Fehler hin. Anleitung dazu:
http://stefanfrings.de/esp8266/index.html#qtcreator
http://stefanfrings.de/avr_tools/index.html#qtcreator

: Bearbeitet durch User
Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Läuft also. Dein Problem  muss irgendwie mit dem Arduino Framework zu
>tun haben.

Danke, da muss ich wohl noch ein wenig forschen.
Mit dem ESP8266 Framework ist es alive, kommen aber keine Tiernamen.
Mit dem Arduino-Uno kann ich nicht kompilieren, weil der "vector" nicht 
kennt.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Mit dem Arduino-Uno kann ich nicht kompilieren, weil der "vector" nicht
> kennt.

Der avr-gcc hat leider keine Standard C++ Libraries.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin einen Schritt weiter: Es funktioniert, wenn ich die beiden 
Objectinstanzierungen vom *.ino in das Tier.cpp File schiebe.

Es scheint an der Besonderheit des *.ino-Files zu liegen.
================== *.h =========================
#include "Arduino.h"
#include <vector>

class Tier
{
    String name;
    
  public:
    Tier(String _name);
    virtual std::vector<Tier*> getObjects(int get);
    void show();
    void showAll();
};

extern std::vector<Tier*> tiere;

void showAll();

================== *.cpp =========================
#include "Tier.h"

std::vector<Tier*> tiere;

Tier::Tier(String _name)
{
  name = _name;
  tiere.push_back(this);
  Serial.println("new anmial born:");
  Serial.println(_name);
}

std::vector<Tier*> Tier::getObjects(int get)
{
  return tiere;
}

void Tier::show()
{
  Serial.println(name);
}

void Tier::showAll()
{
  for (int n = 0; n < tiere.size(); n++) 
  {
    Serial.println(n);
    tiere[n]->show();
  }
}

Tier hund("bello");
Tier katze("miau");


void showAll()
{
  for (int n = 0; n < tiere.size(); n++) 
  {
    Serial.println(n);
    tiere[n]->show();
  }
}



================== *.ino =========================
#include "Tier.h"

void setup() 
{
  Serial.begin(115200);
  delay(10);
  Serial.println("go");
  Serial.print("Anzahl Tiere:");
  Serial.println(tiere.size());
}

void loop() 
{
  Serial.println("alive");
  delay(1000);
  showAll();
}

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Reihenfolge, in der globale Variablen aus unterschiedlich Source 
Files initialisiert  werden, ist in C++ nicht festgelegt. Wenn die 
einzelnen Tiere vor dem Vektor initialisiert werden, wird auf einen 
Vektor im ungültigen Zustand zugegriffen... dann passiert beliebiger 
Unfug.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Die Reihenfolge, in der globale Variablen aus unterschiedlich Source
> Files initialisiert  werden, ist in C++ nicht festgelegt.

Wow, das leuchtet ein. Bei Java ist das einfacher, da muss man die 
Reihenfolge nicht beachten.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gibt's da überhaupt globale Variablen?

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Gibt's da überhaupt globale Variablen?

Sicher doch: hund, katze und tiere.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was haben diese Namen jetzt mit Java zu tun?

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Was haben diese Namen jetzt mit Java zu tun?

Ich denke, Stefanus bezieht sich hier auf das C++ Programm.

Wenn ich die Definition des C++-Vectors vor die Tierinstanzierung 
schreibe, funktioniert es:
#include "Tier.h"

std::vector<Tier*> tiere;

Tier hund("bello");
Tier katze("miau");

void setup() 
{
  Serial.begin(115200);
  delay(10);
  Serial.println("go");
  Serial.print("Anzahl Tiere:");
  Serial.println(tiere.size());
}

void loop() 
{
  Serial.println("alive");
  delay(1000);
  showAll();
}


>Die Reihenfolge, in der globale Variablen aus unterschiedlich Source
>Files initialisiert  werden, ist in C++ nicht festgelegt. Wenn die
>einzelnen Tiere vor dem Vektor initialisiert werden, wird auf einen
>Vektor im ungültigen Zustand zugegriffen... dann passiert beliebiger
>Unfug.

Das finde ich höchst erstaunlich, weil ja bei der Instanzierung die 
Konstrukturen auf jeden Fall aufgerufen werden. Dort wird "vector" 
verwendet und es ist seltsam, dass das möglich ist, obwohl "vector" 
nicht initialisiert ist.
#include "Arduino.h"
#include <vector>

class Tier
{
    String name;
    
  public:
    Tier(String _name);
    void show();
};

extern std::vector<Tier*> tiere;

// this function is not part of the class
void showAll();

==================== *.cpp ==========================
#include "Tier.h"

Tier::Tier(String _name)
{
  name = _name;
  tiere.push_back(this);
  Serial.println("new anmial born:");
  Serial.println(_name);
}

void Tier::show()
{
  Serial.println(name);
}

// this function is not part of the class
void showAll()
{
  for (int n = 0; n < tiere.size(); n++) 
  {
    Serial.println(n);
    tiere[n]->show();
  }
}

Allerdings spricht für die These der nicht vollständigen 
Initilaisierung, dass das Serial.print im Konstruktor scheinbar noch 
nicht initialisiert ist, weil "new animal born" nicht im Terminal 
erscheint.

( Bezogen auf ESP8266 )

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Gibt's da überhaupt globale Variablen?

Es gibt da statische Variablen. Davon gibt's, genau wie bei globalen, 
genau 1 Instanz pro Programmlauf; anders als globale Variablen sind sie 
aber nicht überall, sondern nur in einer Klasse sichtbar. Wie Java 
sicherstellt dass diese Variablen in der richtigen Reihenfolge 
initialisiert werden - insbesondere bei zirkulären Abhängigkeiten - weiß 
ich auch nicht...

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Das finde ich höchst erstaunlich, weil ja bei der Instanzierung die
> Konstrukturen auf jeden Fall aufgerufen werden

Ja, aber in welcher Reihenfolge! Es wird bei dir wohl erst der 
Konstruktor der Tiere aufgerufen, und dann der vom vector. Die 
Reihenfolge ist hier nicht definiert.

Cornelius schrieb:
> Das finde ich höchst erstaunlich

Es ist aber so. Steht im Standard und ist auch zu ergoogeln. Überleg 
mal, wie der Linker wissen soll, dass er den vector-Konstruktor vor dem 
Tier-Konstruktor aufrufen soll...

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, Ich versuche "vector" los zu werden und die Tiere in einem Array zu 
halten.
Das Array sollte ja von Anfang an da sein und ohne "vector" sollte es 
auch auf dem Uno compilieren.
========================= *.h =========================
#ifndef _TIERH_
#define _TIERH_

#include "Arduino.h"
#include <vector>

class Tier
{
    String name;
    
  public:
    Tier();
    Tier(String _name);
    void show();
};

//extern std::vector<Tier*> tiere;

extern Tier tiere[10];
extern int tierPointer;


// this function is not part of the class
void showAll();

#endif // _TIERH_
=========================*.cpp=========================
#include "Tier.h"

Tier tiere[10];
int tierPointer=0;

Tier::Tier()
{
  name="none";
}
Tier::Tier(String _name)
{
  name = _name;
  tiere[tierPointer++]=*this;
}

void Tier::show()
{
  Serial.println(name);
}

// this function is not part of the class
void showAll()
{
  for (int n = 0; n < tierPointer; n++)
  {
    Serial.print(n); Serial.print(" ");
    tiere[n].show();
  }
}
=========================*.ino=========================
#include "Tier.h"

Tier hund("Bello");
Tier katze("Mia");

void setup()
{
  Serial.begin(115200);
  delay(10);

  Serial.println("go");
  Serial.print("Anzahl Tiere:");
  Serial.println(tierPointer);
}

void loop()
{
  Serial.println("alive");
  delay(1000);
  showAll();
  Serial.println();
}


Leider werden alle Tiere nur mit "none" ausgedruckt.
Wie kann das denn sein?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> extern std::vector<Tier*> tiere;

Cornelius schrieb:
> Tier tiere[10];

Bitte den Unterschied zwischen Zeiger und "direkter" Variable lernen.

Cornelius schrieb:
> tiere[tierPointer++]=*this;

Hier wird eine Kopie angelegt. String kann möglicherweise nicht 
kopiert werden.

Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bloss mal als Anregung: Du greifst innerhalb der Klasse
Tier
munter auf die globale Variable
tiere
zu. Ich kann dir nur dringend raten, die Kapitel
über objektorientierte Programmierung (insbesondere
Kapselung etc) in deinem C++-Buch zu lesen, die
du ausgelassen hast. Sonst wirst du mit C++ keinen
Spass haben.

merciless

: Bearbeitet durch User
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine einfachere und effizientere Möglichkeit wäre ganz schlicht:
#include <iostream>

class Tier {
  public:
    constexpr Tier (const char* name);
    void show () const { std::cout << m_name << std::endl; }
  protected:
    const char* m_name;
};

constexpr Tier::Tier (const char* name) : m_name (name) {}
    
constexpr Tier tiere [] = {
  { "Hund" },
  { "Katze" },
  { "Maus" }
};

void showAll () {
  for (auto& tier : tiere) tier.show ();
}

int main () {
  showAll ();
}

Wenn man das std::cout auf dem Arduino durch Serial.print ersetzt, 
erhält man einen Code der komplett ohne dynamische Speicherverwaltung 
auskommt. Dank constexpr braucht man nicht einmal RAM für das Array (für 
AVR brauchts noch PROGMEM). Beim Start müssen dann auch keinerlei 
Konstruktoren laufen, die umständlich Speicher initialisieren.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Eine einfachere und effizientere Möglichkeit wäre ganz schlicht:

Ich weiß nicht, ob das einfacher zu nennen ist.
Mein Ziel ist eher "optischer" Natur.

Ich will, dass die Klassenerzeugung im *.ino File genau so aussieht:

Tier hund("bello");
Tier katze("miau");

void setup() 
{
  Serial.begin(115200);
}

void loop() 
{
  Serial.println("alive");
  delay(1000);
  showAll();
}


Es geht mir darum, für den Benutzer eine möglichst einfache API zu 
schaffen.
Ob es da grundsätzliche Hindernisse gibt, weiß ich nicht. Ich vermute 
aber, dass es ein Möglichkeit zur Lösung gibt.

Ich werde weiter forschen ...

Autor: Dirk K. (merciless)
>zu. Ich kann dir nur dringend raten, die Kapitel
>über objektorientierte Programmierung (insbesondere
>Kapselung etc) in deinem C++-Buch zu lesen, die
>du ausgelassen hast. Sonst wirst du mit C++ keinen
>Spass haben.

Das Du mir Ratschläge geben kannst, zeigst Du am ehesten durch ein 
Beispiel, welches das Problem löst.
So weit ich sehen kann, hat hier bis jetzt noch niemand eine Lösung.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Ich will, dass die Klassenerzeugung im *.ino File genau so aussieht:

Und was, wenn der Nutzer mehrere Listen an Tieren haben will? Vielleicht 
gibt es ja mehr als einen Zoo. Dass sich ein Objekt bei der Erstellung 
automatisch in eine "versteckte" Liste/Array einfügt ist nicht 
wartungsfreundlich oder sauber. Manchmal ist die kürzeste Lösung nicht 
die Beste.

Dein einzige Art, den Code (fast) so kurz zu machen wie gewünscht, ist 
es den vector in der selben Datei wie "hund" und "katze" anzulegen, und 
zwar vor diesen beiden Variablen.

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder die Liste im Konstruktor mitgeben, als Referenz, dann existiert die 
auf jeden Fall.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Oder die Liste im Konstruktor mitgeben, als Referenz, dann
> existiert die
> auf jeden Fall.

Nicht, wenn man die Referenz über eine Forward-Declaration mitgibt.

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pssst, das weckt nur die C++ Kritiker.

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Es geht mir darum, für den Benutzer eine möglichst einfache API zu
> schaffen.

Dann macht man eine Klasse Zoo, die als Factory für Tiere benutzt wird. 
Sprich: du erzeugst die Tiere nicht selber, sondern überlässt das dem 
Zoo, dder die Tiere dann auch direkt in die Liste einträgt.
class Tier {
  String name;
};

class Zoo {
public:
  Tier *neues_Tier(String name);
private:
  int nr_tiere;
  Tier alle_tiere[10];
}

Tier * Zoo::neues_Tier(String name) {
  if(nr_tiere >= 10) {
    cout << "Zoo voll!" << endl;
    return 0;
  } else {
    alle_tiere[nr_tiere].name = name;
    nr_tiere ++;
    return &alle_tiere[nr_tiere - 1];
  }
}

int main() {
  Zoo mein_zoo;
  Tier * hund  = mein_zoo.neues_tier("bello");
  Tier * katze = mein_zoo.neues_tier("miau");
  Tier * maus  = mein_zoo.neues_tier("pieps");
}

Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Autor: Dirk K. (merciless)
>>zu. Ich kann dir nur dringend raten, die Kapitel
>>über objektorientierte Programmierung (insbesondere
>>Kapselung etc) in deinem C++-Buch zu lesen, die
>>du ausgelassen hast. Sonst wirst du mit C++ keinen
>>Spass haben.
>
> Das Du mir Ratschläge geben kannst, zeigst Du am ehesten durch ein
> Beispiel, welches das Problem löst.
> So weit ich sehen kann, hat hier bis jetzt noch niemand eine Lösung.

Dr. Sommer hat doch direkt nach mir eine
saubere Lösung präsentiert, die sogar noch
auf die Besonderheiten der Speicherverwaltung
bei uCs eingeht.

Eine Klasse legt man an, damit man diese in
anderen Projekten wiederverwenden kann. Deine
Klasse Tier kann man nicht wiederverwenden,
ohne eine globale Variable tiere vom Typ
std::vector<Tier*> zu deklarieren. Solche
Abhängigkeiten löst man durch Dependency
Injection o.ä. Da dies aber den Rahmen hier
sprengen würde und ich nicht glaube, dass du
momentan mit solchen Erklärungen etwas anfangen
kannst, habe ich nur vorsichtig darauf hinweisen
wollen, dass dir Grundkenntnisse über OOP fehlen.

merciless

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Dr. Sommer schrieb:
>> Die Reihenfolge, in der globale Variablen aus unterschiedlich Source
>> Files initialisiert  werden, ist in C++ nicht festgelegt.
>
> Wow, das leuchtet ein. Bei Java ist das einfacher, da muss man die
> Reihenfolge nicht beachten.

Rolf M. schrieb:
> Was haben diese Namen jetzt mit Java zu tun?

Niemand hat gesagt, das die Namen mit Java zu tun haben.

Bei Java hat man automatisch keine Probleme mit der Reihenfolge der 
Initialisierung. Bei C++ muss man etwas mehr mitdenken. Darum ging es.

: Bearbeitet durch User
Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Dr. Sommer hat doch direkt nach mir eine
>saubere Lösung präsentiert, die sogar noch
>auf die Besonderheiten der Speicherverwaltung
>bei uCs eingeht.

Aber falls ich mich nicht täusche, kann ich damit nicht im *.ino File 
nach Arduino-Style anlegen.
Wie ich schon sagte, der Main-Code soll genau so aussehen:
Beitrag "Re: C++ Arduino Grundlagen"

Meines Erachtens ist das der Syntax mit der maximalen Klarheit und 
Einfachheit.
Arduino heißt: Die Maschine dient dem Menschen. Der Syntax soll so 
einfach sein, dass es für den Menschen einfach wird, nicht für die 
Maschine.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich habe jetzt die ultimative Lösung maximaler Einfachheit erreicht.
Besser wird's nicht gehen.
Der Code läuft jetzt auch auf einem Arduino Uno und zwar ohne "vector" 
oder ähnlichen Schnick-Schnack zu benötigen.

Ich greife dazu in die Informatiker Trickkiste und verwende verkettete 
Listen.
======================= *.h ================================
#ifndef _TIERH_
#define _TIERH_

#include "Arduino.h"

class Tier
{
    public:

    String name;
    static Tier *merkerTier;
    Tier *nextTier;
    Tier *lastTier;
    
    static int tierZaehler;

    Tier();
    Tier(String _name);
    void show();
};

// this function is not part of the class
void showAll();
int getAnzahlTiere();

#endif // _TIERH_
========================== *.cpp =============================
#include "Tier.h"

int Tier::tierZaehler=0;
Tier *Tier::merkerTier=NULL;

Tier::Tier()
{
  name="none";
  nextTier=NULL;
  lastTier=NULL;
}

Tier::Tier(String _name)
{
    name = _name;

    if(merkerTier)merkerTier->nextTier=this; // der pointer vom letzten Tier soll auf dieses Object zeigen
    lastTier=merkerTier; // das letze Tier hier ist von vorher
    merkerTier=this; // jetzt dieses Tier merken

    tierZaehler++;
}

void Tier::show()
{
  Serial.println(name);
}

// this function is not part of the class
void showAll()
{
  Tier * t = Tier::merkerTier;

  while(t)
  {
    t->show();
    t=t->lastTier;
  }

}

int getAnzahlTiere()
{
  return Tier::tierZaehler;
}

========================== *.ino =============================
#include "Tier.h"

Tier hund("Bello");
Tier katze("Mia");
Tier vogel("Flippy");

void setup()
{
  Serial.begin(115200);
  delay(10);

  Serial.println("go");
  Serial.print("Anzahl Tiere:");
  Serial.println(getAnzahlTiere());

}

void loop()
{
  Serial.println("animals alive!!");
  delay(1000);
  showAll();
  Serial.println();
}





Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
list<Tier *> wäre wohl zu kompliziert gewesen O.o

merciless

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Cornelius schrieb:
> Besser wird's nicht gehen.

Besser als falsch gehts immer. falsch, weil die statische Variable 
nach dem Aufruf der Konstruktoren initialisiert werden könnte. Nur 
weil es gerade zufällig den Anschein hat zu funktionieren, ist es noch 
lange nicht richtig.

Cornelius schrieb:
> Arduino heißt: Die Maschine dient dem Menschen. Der Syntax soll so
> einfach sein, dass es für den Menschen einfach wird, nicht für die
> Maschine.
Dann nimm Python oder Ruby. C und C++ haben eine viel kompliziertere 
Syntax. Außerdem ging es den Einwänden darum, dass der Mensch (du) den 
Code später sinnvoll wieder verwenden können soll. Du legst dir aber 
selbst Steine in den Weg: Du hast nur genau 1 Liste von Tieren pro 
Programm. Du kannst nie einen 2. Zoo hinzufügen. Oder Tiere komplett 
unabhängig von der Liste anlegen. Das Tier trägt sich heimlich hinter 
den Kulissen in eine Liste ein; wenn du eine Katze kaufst, möchtest du 
dann auch dass sie eine unsichtbare Schnur hinter sich her zieht mit der 
sie sich mit anderen Katzen verheddert? Ein Objekt, eine C++ Klasse, 
sollte so gut wie möglich für sich alleine stehen können.

Erfahrene Programmierer wissen so etwas; du kannst solchen Rat entweder 
direkt annehmen oder später durch Schmerz lernen, warum man so etwas 
nicht macht...

Cornelius schrieb:
> #ifndef _TIERH_

Bezeichner mit Unterstrich+Großbuchstabe sind verboten.

Cornelius schrieb:
> int Tier::tierZaehler=0;

Wenn schon std::size_t für Array-Größen; "int" könnte zu klein sein.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk K. schrieb:
> list<Tier *> wäre wohl zu kompliziert gewesen O.o

Da er die list, vector oder was auch immer unbedingt in einer anderen 
Translation Unit als die globalen Instanzen haben möchte, sind alle 
Varianten falsch; nur äußert sich der Fehler der intrusiven Variante mit 
Pointern in der Klasse selbst später. Symptom-Behandlung halt.

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vector, list und co. möchte er gar nicht haben um kompatibel mit AVR zu 
sein für die es das nicht gibt, aber da würde ich mir eher überlegen ob 
man sich noch an die historische HW klammern muss. Die std container 
bieten ja mehr Luxus als nur Elemente einzufügen, löschen, suchen, 
indizierte Listen sind ja alles nur leichte Variationen im Code.
Das Factory Pattern würde ich auch bevorzugen, das das Tier seine 
Verwaltung kennen muss ist nicht gut.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Das Factory Pattern würde ich auch bevorzugen, das das Tier seine
> Verwaltung kennen muss ist nicht gut.

Sehe ich auch so. Die Tiere verwalten nicht den Zoo, sondern anders 
herum.

Also kann ein Zoo gerne eine Methode wie nehmeTierAuf() haben, aber das 
Tier sollte keinerlei Methoden enthalten, die irgend etwas mit dem Zoo 
zu tun haben.

Tiere sollte es auch ohne Zoo geben.

Was machst du, wenn manche Tiere im Zoo, manche in der Wildnis und mache 
im Haushalt sind?

Autor: Dirk K. (merciless)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> vector, list und co. möchte er gar nicht haben um kompatibel mit AVR zu
> sein für die es das nicht gibt, aber da würde ich mir eher überlegen ob
> man sich noch an die historische HW klammern muss. Die std container
> bieten ja mehr Luxus als nur Elemente einzufügen, löschen, suchen,
> indizierte Listen sind ja alles nur leichte Variationen im Code.
> Das Factory Pattern würde ich auch bevorzugen, das das Tier seine
> Verwaltung kennen muss ist nicht gut.
Das meinte ich ja vorhin mit meinem Einwand
"Vielleicht erstmal das Kapitel über OOP lesen".

Cornelius schreibt schlecht strukturierten C-Code
im C++-Gewand. Aber wenn er sich nicht helfen lassen will...

merciless

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich weiß, für euch ist das Design-Ziel schwer zu verstehen:
Es geht um die optimale User-API.
Das Optimierungkriterium heißt: "Einfachheit für den User"

Und die sieht nach meiner Meinung so aus:
#include "Tier.h"

Tier hund("Bello");
Tier katze("Mia");
Tier vogel("Flippy");

void setup()
{
  Serial.begin(115200);
  delay(10);
}

void loop()
{
  Serial.println("animals alive!!");
  delay(1000);
  showAll();
  Serial.println();
}

Wenn es bessere gibt, nur zu. Konstruktive Vorschläge werden gerne 
untersucht.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wer ist der "User"? Sicher ein Programmierer. So ein beschränktes 
Framework würde mir keine Freude bereiten.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Dann nimm Python oder Ruby.
Nein, ich bin sozusagen Werkzeugmacher: Ich mache es für andere 
einfacher.

> C und C++ haben eine viel kompliziertere
>Syntax.

So ist es. Und deshalb hat Wiring den Syntax auf eine einfache Form 
reduziert. Deshalb gibt es Arduino. Deshalb ist es so erfolgreich: Weil 
es mit alten Weisheiten gebrochen hat.

>Außerdem ging es den Einwänden darum, dass der Mensch (du) den
>Code später sinnvoll wieder verwenden können soll.

Er wird sinnvoll verwendet werden.

>Du legst dir aber
>selbst Steine in den Weg: Du hast nur genau 1 Liste von Tieren pro
>Programm. Du kannst nie einen 2. Zoo hinzufügen.

Ich will keinen zweiten Zoo. In meiner Stadt ist einer genug.

>Oder Tiere komplett
>unabhängig von der Liste anlegen. Das Tier trägt sich heimlich hinter
>den Kulissen in eine Liste ein; wenn du eine Katze kaufst, möchtest du
>dann auch dass sie eine unsichtbare Schnur hinter sich her zieht mit der
>sie sich mit anderen Katzen verheddert?

Kein Tier darf hier frei rumlaufen. Alle müssen in den Zoo.

> Ein Objekt, eine C++ Klasse,
> sollte so gut wie möglich für sich alleine stehen können.

Versuch das mal mit der Arduino TwoWire-Klasse

>Erfahrene Programmierer wissen so etwas; du kannst solchen Rat entweder
>direkt annehmen oder später durch Schmerz lernen, warum man so etwas
>nicht macht...

Was C++ anbelangt, kann man mich wahrscheinlich nicht als erfahrenen 
Programmierer bezeichnen. Was einige andere Programmiersprachen angeht 
mit Sicherheit schon.

>Cornelius schrieb:
>> #ifndef _TIERH_
>Bezeichner mit Unterstrich+Großbuchstabe sind verboten.

Das hat wizziger Weise Eclipse-CDT automatisch beim Anlegen der Klasse 
erzeugt. Vielleicht sind die nicht auf dem neusten Stand.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Es geht um die optimale User-API.

Ein fehlerhaftes API welches beim nächsten Compiler Update nicht mehr 
funktioniert ist bestimmt nicht optimal.

Cornelius schrieb:
> Wenn es bessere gibt, nur zu

Wurden dir ausreichend genannt. Wenn du unbedingt fehlerhafte Software 
machen möchtest, bitte schön.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Und deshalb hat Wiring den Syntax auf eine einfache Form reduziert

Wiring ist immer noch C++ und hat genau die gleich komplizierte Syntax.

Cornelius schrieb:
> Ich will keinen zweiten Zoo. In meiner Stadt ist einer genug.

Genau wie 64 KB RAM für alle genug sind, und IPv4-Adressen für alle 
ausreichen? So ziemlich das wichtigste bei Software-Architekturen ist 
es, zukunftssicher und flexibel zu bleiben. Das ist man mit einer fixen 
versteckten Datenstruktur nicht. Früher oder später will man doch 
irgendwann mal 2 Zoos haben. Globale Datenstrukturen von denen man nur 1 
hat sind ein absolutes No-Go.

Cornelius schrieb:
> Versuch das mal mit der Arduino TwoWire-Klasse

Arduino ist gewiss nicht Beispielhaft. Bei Peripherie-Ansteuerung kann 
man vielleicht noch Ausnahmen machen, aber für eine Software-Only 
Struktur wie deinen Zoo nicht.

Cornelius schrieb:
> Was einige andere Programmiersprachen angeht mit Sicherheit schon.

Dann kannst du doch die hoffentlich da erlernten guten Vorgehensweisen 
übernehmen.

Cornelius schrieb:
> Vielleicht sind die nicht auf dem neusten Stand.

Diese Regel ist nicht neu. Bei mir macht CDT das nicht...

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Ich bin sozusagen Werkzeugmacher:
> Ich mache es für andere einfacher.

Du machst dich lächerlich.

> Deshalb ist es so erfolgreich: Weil
> es mit alten Weisheiten gebrochen hat.

Du hast doch keine Ahnung, warum Arduino erfolgreich ist. Diese Meinung 
mag für Dich persönlich zutreffen, das glaube ich Dir gerne. Aber so 
allgemein-gültig wie du das geäußert hast, kann man das nicht stehen 
lassen.

Mein persönlicher Eindruckt ist, dass Arduino genau wie Youtube die 
Leute verdummen lässt. Sie bilden sich ein, dass man mit ein paar Klicks 
hier und ein paar Strippen da alles besser machen kann, als die 
vorherige Generation. Und dann kommen sie angekrochen und stellen 
Fragen, weil sie an den Grenzen ihres Flickwerks scheitern. Das kannst 
du nicht abstreiten, denn es ist Dir gerade selbst auch passiert.

Sei mal ein bisschen demütiger. Dir fehlt Erfahrung.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Rolf M. schrieb:
>> Was haben diese Namen jetzt mit Java zu tun?
>
> Niemand hat gesagt, das die Namen mit Java zu tun haben.

Ich hatte gefragt, ob es in Java globale Variablen gibt, und als Antwort 
darauf nanntest du die Tiernamen.

> Bei Java hat man automatisch keine Probleme mit der Reihenfolge der
> Initialisierung. Bei C++ muss man etwas mehr mitdenken. Darum ging es.

Worum es ging war, dass bei C++ die Initialisierungsreihenfolge globaler 
Variablen nicht immer definiert ist. Du meintest, dass man sich bei Java 
im Gegensatz dazu keine Gedanken über die Reihenfolge machen muss. 
Soweit ich mich erinnere, gibt's aber in Java gar keine globalen 
Variablen, so dass es gar nicht erst eine Reihenfolge gibt, mit der man 
Probleme bekommen könnte. Da mich diese Erinnerung aber trügen könnte, 
habe ich nachgefragt.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Cornelius schrieb:
>> Vielleicht sind die nicht auf dem neusten Stand.
>Dr.Sommer schrieb
>Diese Regel ist nicht neu. Bei mir macht CDT das nicht...

Ich hab's grad noch mal nach kontrolliert: Du hast Recht. Es erzeugt die 
Klassen ohne Unterstrich am Anfang. Da ist mir die Hand-geschriebene 
wohl rein gerutscht.

int Tier::tierZaehler=0;

>Besser als falsch gehts immer. falsch, weil die statische Variable
>nach dem Aufruf der Konstruktoren initialisiert werden könnte. Nur
>weil es gerade zufällig den Anschein hat zu funktionieren, ist es noch
>lange nicht richtig.

Das ist ein guter Hinweis. Ich bin davon ausgegangen, dass statische 
Variablen mit Vorinitialisierung genau wie in C zur Compilezeit 
initialisiert werden.

Die Frage ist: Wie wäre eine sichere Implementierung?

Theoretisch könnte ich auf den Tierzähler verzichten, wenn ich eine 
Funktion schreibe, die jedes mal die List durchiteriert und zählt. Das 
würde den Speicher für die Variable sparen, dafür aber Zeit kosten.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Die Frage ist: Wie wäre eine sichere Implementierung?

Dr. Sommer schrieb:
> Dein einzige Art, den Code (fast) so kurz zu machen wie gewünscht, ist
> es den vector in der selben Datei wie "hund" und "katze" anzulegen, und
> zwar vor diesen beiden Variablen.

Oder

Johannes S. schrieb:
> Oder die Liste im Konstruktor mitgeben, als Referenz, dann existiert die
> auf jeden Fall.
(Aber vorher nicht nur eine Forward declaration)

Oder

Dr. Sommer schrieb:
> Eine einfachere und effizientere Möglichkeit wäre ganz schlicht:

Diese Variante geht natürlich auch mit vector, list, array, und ist 
m.M.n. die sauberste - der Leser sieht sofort dass die Tiere in einen 
Zoo gepackt werden, wie groß dieser ist, wie schnell der Zugriff ist...

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
>>falsch, weil die statische Variable nach dem Aufruf der Konstruktoren
>>initialisiert werden könnte. Nur weil es gerade zufällig den Anschein hat
>>zu funktionieren, ist es noch lange nicht richtig.
>
> Das ist ein guter Hinweis. Ich bin davon ausgegangen, dass statische
> Variablen mit Vorinitialisierung genau wie in C zur Compilezeit
> initialisiert werden.

Das kommt drauf an. Da es Konstruktoren gibt, kann es ja auch Code 
geben, der zur Initialisierung ausgeführt werden muss. In C ist die 
Initialisierung globaler Variablen immer statisch, in C++ kann sie je 
nach Situation statisch oder dynamisch sein.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Ich hatte gefragt, ob es in Java globale Variablen gibt, und als Antwort
> darauf nanntest du die Tiernamen.

Ja, so ähnlich. Es gibt keine Variablen außerhalb von Klassen, aber man 
kann die Member der Klassen als "static" kennzeichnen, dann sind sie 
immer und überall erreichbar, ohne eine Instanz von der Klasse erstellen 
zu müssen.

In manchen Projekte lege ich dafür extra eine Dummy Klasse an, die 
Globals heißt und als Sammelpunkt für alle quasi globalen Variablen 
dient.

Ich möchte aber darauf hinweisen, das man globale Variablen in 
objektorientierten Desktop Programmen nur spärlich und mit Bedacht 
einsetzen sollte. Bei Mikrocontrollern benutze ich mehr globale 
Variablen, wegen der beschränkten Speicherverwaltung.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Es gibt keine Variablen außerhalb von Klassen, aber man
> kann die Member der Klassen als "static" kennzeichnen, dann sind sie
> immer und überall erreichbar, ohne eine Instanz von der Klasse erstellen
> zu müssen.
>
> In manchen Projekte lege ich dafür extra eine Dummy Klasse an, die
> Globals heißt und als Sammelpunkt für alle quasi globalen Variablen
> dient.

Das war sowas, das mich an Java schon bei Funktionen gestört hat. Solche 
Dummy-Klassen sind ein schlechter Work-Around für das Fehlen von 
Sprachfeatures.

> Ich möchte aber darauf hinweisen, das man globale Variablen in
> objektorientierten Desktop Programmen nur spärlich und mit Bedacht
> einsetzen sollte. Bei Mikrocontrollern benutze ich mehr globale
> Variablen, wegen der beschränkten Speicherverwaltung.

Da sind die Programme ja meist auch überschaubarer.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
>> In manchen Projekte lege ich dafür extra eine Dummy Klasse an, die
>> Globals heißt und als Sammelpunkt für alle quasi globalen Variablen
>> dient.
>
> Das war sowas, das mich an Java schon bei Funktionen gestört hat. Solche
> Dummy-Klassen sind ein schlechter Work-Around für das Fehlen von
> Sprachfeatures.

Kann man so sehen. Man muss halt Wege finden, mit den Arbeitsmitteln 
klar zu kommen, die zur Verfügung stehen. Es gibt schlimmeres. Irgendwo 
ist immer ein haken.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Kann man so sehen. Man muss halt Wege finden, mit den Arbeitsmitteln
> klar zu kommen, die zur Verfügung stehen.

Java ist halt dazu konzipiert, möglichst einfach und schnell erlernbar 
zu sein. Dazu gehört, dass es für die meisten Dinge nur eine Möglichkeit 
gibt sie umzusetzen, was Verwirrung insbesondere bei Anfängern 
vermeidet. Für Funktionen ohne "this"-Instanz gibt es in Java nur 
statische Funktionen, nicht statische und freie wie in C++. Zum 
Weglassen von Parametern gibt es nur Overloads, keine Default-Parameter. 
Für Callbacks gab es nur Interfaces, keine Funktionszeiger oder so 
(mittlerweile auch Lambdas, aber das sind auch nur Interfaces). In C++ 
gibt es Funktionszeiger, Member-Funktionszeiger (in 12-Facher(!) 
Ausfertigung für die verschiedenen Referenz-Kategorien), std::function, 
Lambdas und Klassen mit operator(). Natürlich ist das mächtiger und 
flexibler, aber auch komplizierter.

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Java ist halt dazu konzipiert, möglichst einfach und schnell
> erlernbar zu sein.
> Natürlich ist das (C++) mächtiger und flexibler, aber auch komplizierter.

Ehrlich gesagt benutze ich sowohl von Java als auch von C++ immer noch 
nur einen Bruchteil der Sprach-Features, obwohl ich beide Sprachen 
beruflich seit über 10 Jahren einsetze. Und das finde ich so auch in 
Ordnung. Man muss nicht alles machen, nur weil es geht.

Autor: Cornelius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
>Diese Variante geht natürlich auch mit vector, list, array, und ist
>m.M.n. die sauberste - der Leser sieht sofort dass die Tiere in einen
>Zoo gepackt werden, wie groß dieser ist, wie schnell der Zugriff ist...

Wenn die Welt so konzipiert ist, dass alle Tiere in den Zoo müssen, ist 
es nicht notwendig, dies extra zu erwähnen:

Der Löwe. Er muss in den Zoo.
Der Fuchs. Er muss in den Zoo.
Der Hamster. Er muss in den Zoo.
Der Emu. Er muss in den Zoo.
Die Katze. Sie muss in den Zoo.

Das sie in den Zoo müssen ist nur ein Tribut an die Maschine.
Unnötige Schreibarbeit und Syntaxkomplexität sollte man sich in einer 
Arduino-Umgebung sparen ( und um die geht es hier laut der Überschrift 
).

Autor: Johannes S. (jojos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cornelius schrieb:
> Unnötige Schreibarbeit und Syntaxkomplexität sollte man sich in einer
> Arduino-Umgebung sparen

ja, genauso wie gute Entwürfe und Lernbereitschaft. Gehört einfach nicht 
in die Arduino Welt :-(
Es funktioniert, also ist es gut.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und was ist, wenn du Unit-Tests schreiben möchtest, und für jeden Test 
einen neuen Zoo brauchst?

Wenn du unbedingt nur den einen Zoo haben willst, dann definiere diesen 
in der selben Datei wie die Tiere, und zwar vor diesen. Das ist 1 Zeile. 
Diese 1 Zeile wird die Komplexität nicht dramatisch steigern. So wird 
der Zoo korrekt vor den Tieren initialisiert.

Tier.h
#include <vector>
#include <string>

class Tier {
  public:
    Tier (std::string name);
    void show ();
  private:
    std::string m_name;
};

extern std::vector<Tier*> zoo;
void showAll ();

Tier.cpp
#include "Tier.h"

Tier::Tier (std::string name) : m_name (name) {
  zoo.push_back (this);
}

void Tier::show () { std::cout << m_name; }

void showAll () {
  for (auto& t : zoo) t.show ();
}

Main.cpp
#include "Tier.h"

std::vector<Tier*> zoo;

Tier katze ("Katze"),
     kuh ("Kuh"),
     tiger ("Tiger");

int main () {
  showAll ();
}

Jetzt kannst du Tiere natürlich nur noch in der Main.cpp global 
definieren, Tiere nicht mehr entfernen, und keinen neuen Zoo anlegen. 
Aber jetzt ist es so "einfach" wie du es haben wolltest, und immerhin 
nicht fehlerhaft.

Cornelius schrieb:
> Das sie in den Zoo müssen ist nur ein Tribut an die Maschine.
Das sie Speicher brauchen ist ein Tribut. Die Maschine braucht den Zoo 
nicht. Du willst diese Datenstruktur haben, sie scheint mit dem 
Problem zusammen zu hängen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.