[Arduino_IDE; gcc]Frage zu C++-Konstruktor

ManniP

Grünschnabel
Hallo,
die Frage hat sich in der IDE ergeben, betrifft aber den Compiler selbst, deshalb stelle ich sie hier:
Es geht um folgenden Code:
C++:
void RGB_LED_Class::RGB_LED_Class(int Pin_Red, int Pin_Green, int Pin_Blue)
{
   RGB_Pin[red] = Pin_Red;
   RGB_Pin[green] = Pin_Green;
   RGB_Pin[blue] = Pin_Blue;
   RGB_Init(red);
   RGB_Init(green);
   RGB_Init(blue);
   RGB_State[red] = Off;
   RGB_State[green] = Off;
   RGB_State[blue] = Off;
}

void RGB_LED_Class::RGB_Init(enum Color Couleur)
{
   pinMode(RGB_Pin[Couleur], OUTPUT);
   digitalWrite(RGB_Pin[Couleur], HIGH);
}

void RGB_LED_Class::RGB_Change(enum Color Couleur, enum Flag Zustand)
{
   if(Zustand == Off)
   {
       digitalWrite(RGB_Pin[Couleur], HIGH);
   }
   else
   {
       digitalWrite(RGB_Pin[Couleur], LOW);
   }
   RGB_State[Couleur] = Zustand;
}

enum Flag RGB_LED_Class::Red_on()
{
   RGB_Change(red, On);
}

enum Flag RGB_LED_Class::Green_on()
{
   RGB_Change(green, On);
}

enum Flag RGB_LED_Class::Blue_on()
{
   RGB_Change(blue, On);
}

enum Flag RGB_LED_Class::Red_off()
{
   RGB_Change(red, Off);
}

enum Flag RGB_LED_Class::Green_off()
{
   RGB_Change(green, Off);
}

enum Flag RGB_LED_Class::Blue_off()
{
   RGB_Change(blue, Off);
}
und
C++:
#ifndef RGB_ON_ADAP_H
#define RGB_ON_ADAP_H

class RGB_LED_Class
{
   public:
       enum Flag {On, Off} State;
       enum Color {red, green, blue} Farbe;
   private:
       uint8_t RGB_Pin[3];
       enum Flag RGB_State[3];
       void RGB_Change(enum Color Couleur, enum Flag Zustand);
       void RGB_Init(enum Color Couleur);
   public:
       enum Flag Red_on();
       enum Flag Green_on();
       enum Flag Blue_on();
       enum Flag Red_off();
       enum Flag Green_off();
       enum Flag Blue_off();

};
RGB_LED_Class RGB_Led(45,47,32);
#endif //RGB_ON_ADAP_H
Der Header ist in diesem Code (für die Marlin-3D-Drucker-Software) immer mit einer Definiton einer Variablen gesichert, um doppelte Aufrufe zu vermeiden.

Der Fehler 'no matching function for call to 'RGB_LED_Class::RGB_LED_Class(int, int, int)' ' wird dabei gemeldet, ich erkenne aber nicht, warum er den Konstruktor in der cpp-Datei nicht anerkennt.

Ne Idee?

Tschüß

Manni
 

cwriter

Erfahrenes Mitglied
ich erkenne aber nicht, warum er den Konstruktor in der cpp-Datei nicht anerkennt
Du musst den Konstruktor in der Klasse zuerst deklarieren.
Der Compiler schaut sich immer zuerst den Header an und sucht sich dann die Symbole dazu zusammen. In deinem Fall hast du eine Instanz der Klasse noch im Header instanziiert, aber der Konstruktor kann da gar noch nicht bekannt sein.
RGB_LED_Class RGB_Led(45,47,32);
Das hier ist noch im Header. Soll das so sein?
(Antwort: Nein...)

Der Header ist in diesem Code (für die Marlin-3D-Drucker-Software) immer mit einer Definiton einer Variablen gesichert, um doppelte Aufrufe zu vermeiden.
HeaderInclude Guards sind keine Variablen.
 
Zuletzt bearbeitet:

ManniP

Grünschnabel
Da ich den ganzen Code nicht noch einmal einbauen wollte, habe ich meine Kommentare hier eingebaut. Ich hoffe, man verläuft sich nicht.
Hallo,
die Frage hat sich in der IDE ergeben, betrifft aber den Compiler selbst, deshalb stelle ich sie hier:
Es geht um folgenden Code:
C++:
void RGB_LED_Class::RGB_LED_Class(int Pin_Red, int Pin_Green, int Pin_Blue)
{
   RGB_Pin[red] = Pin_Red;
   RGB_Pin[green] = Pin_Green;
   RGB_Pin[blue] = Pin_Blue;
   RGB_Init(red);
   RGB_Init(green);
   RGB_Init(blue);
   RGB_State[red] = Off;
   RGB_State[green] = Off;
   RGB_State[blue] = Off;
}
Nachdem sich der Compiler nun über das Schlüsselwort 'void' in der Deklaration beschwert hat, habe ich es entfernt. In einem Lehrbuch zu C++ stand der Konstruktor auch ohne Datentypangabe für den Rückgabewert. Ich hatte es im Buch kontrolliert, da ich mir nicht mehr sicher war. Doch nun beschwert er sich, dass die Klassenangabe in der Funktionsdefinition (also der cpp-Datei) keine Typ-Angabe sei ... obwohl er ja keine Typ-Angabe an dieser Stelle will. Ich habe dann auch noch mal die Syntax in dem Lehrbuch nachgelesen, sie stimmt. Klassenname::Klassenname ist die richtige Schreibweise. Also sollte er das auch akzeptieren. Warum also nicht? Ich blick' nicht mehr durch...
Du musst den Konstruktor in der Klasse zuerst deklarieren.
Der Compiler schaut sich immer zuerst den Header an und sucht sich dann die Symbole dazu zusammen. In deinem Fall hast du eine Instanz der Klasse noch im Header instanziiert, aber der Konstruktor kann da gar noch nicht bekannt sein.
Genau deshalb hatte ich die Frage gestellt. Ich war irgendwie betriebsblind und merkte nicht, wo ich den Fehler gemacht hatte. Ich hatte aber schon vermutet, dass er ganz banal sein würde ... ist er meistens.
Das hier ist noch im Header. Soll das so sein?
(Antwort: Nein...)
Eigentlich schon ... so war wenigstens die Idee. Denn eine Variable von einem Klassen-Typ kann ja direkt innerhalb der Definition der Klasse auch deklariert werden. Das das mit einem Konstruktor-Aufruf verbunden nicht geht hatte ich nicht erwartet.

HeaderInclude Guards sind keine Variablen.
Stimmt. Tschuldigung. Ich musste weg und hatte die Frage auf die Schnelle eingegeben. Den korrekten Namen dafür kannte ich allerdings auch nicht.

Ergänzung: Nachdem ich in der cpp-Datei ein include für den Header ergänzt hatte (ich hatte geglaubt, der Header in der Hauptheaderdatei des Projekts würde ausreichen) hat es soweit funktioniert. Doch jetzt beschwert der Linker sich über mehrfache Definitionen der Variable RGB_Led (der Instanz der Klasse direkt in der Klassendefinition hinten). Doch ich werde diese Variable in vielen der Projektdateien brauchen. Deshalb habe ich sie ja an dieser Stelle definiert.. Wenn ich sie in einzelnen Projektdateien definiere, ist sie anderswo ja nicht bekannt. Wie kann ich nun den Linker zufrieden stellen, wenn ich die Variable in verschiedenen cpp-Dateien verwenden möchte und es nur eine (global) geben soll?

Tschüß

Manni
 

ManniP

Grünschnabel
Noch eine Ergänzung: Natürlich experimentiere ich hier auch parallel weiter. Dabei habe ich die Deklaration der Variablen aus der Header-Datei entfernt und in Marlin_main.cpp aufgenommen. Ich wollte dann in den anderen Dateien, wo ich sie verwenden möchte, mit extern-Deklarationen arbeiten. Doch auch das klappt nicht. In Marlin_main ist Marlin.h includiert, da wiederum sind andere Header drin -auch die Neue. Trotzdem meldet der Linker folgendes:

Linking everything together...
"C:\Program Files\arduino-1.8.1\hardware\tools\avr/bin/avr-gcc" -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections,--relax -mmcu=atmega2560 -o "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105/Marlin.ino.elf" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\G26_Mesh_Validation_Tool.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\M100_Free_Mem_Chk.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\Marlin.ino.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\MarlinSerial.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\Marlin_main.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\RGB_On_Adap.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\Sd2Card.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\SdBaseFile.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\SdFatUtil.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\SdFile.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\SdVolume.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\blinkm.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\cardreader.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\configuration_store.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\dac_mcp4728.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\digipot_mcp4018.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\digipot_mcp4451.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\endstops.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\hex_print_routines.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\least_squares_fit.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\mesh_bed_leveling.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\nozzle.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\planner.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\planner_bezier.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\printcounter.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\qr_solve.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\serial.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\servo.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\stepper.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\stepper_dac.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\stepper_indirection.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\stopwatch.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\temperature.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\twibus.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\ubl.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\ubl_G29.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\ubl_motion.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\ultralcd.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\utility.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\vector_3.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch\watchdog.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\libraries\LiquidCrystal\LiquidCrystal.cpp.o" "C:\Users\Admin\AppData\Local\Temp\arduino_build_788105/core\core.a" "-LC:\Users\Admin\AppData\Local\Temp\arduino_build_788105" -lm
C:\Users\Admin\AppData\Local\Temp\cc8xA7st.ltrans14.ltrans.o: In function `__static_initialization_and_destruction_0':

C:\Users\Admin\AppData\Local\Temp\arduino_build_788105\sketch/Marlin_main.cpp:291: undefined reference to `RGB_LED_Class::RGB_LED_Class(int, int, int)'

collect2.exe: error: ld returned 1 exit status

Bibliothek LiquidCrystal in Version 1.0.5 im Ordner: C:\Program Files\arduino-1.8.1\libraries\LiquidCrystal wird verwendet
exit status 1


Ich habe die Linker-Liste mit Absicht mit eingefügt (obwohl sie unübersichtlich lang ist), da sieht man dann, dass auch RGB_On_Adap.cpp als entsprechende Objekt-Datei mit in der Liste steht. Warum er das Symbol nicht erkennt, weis ich wieder mal nicht. Vor allem, weil der Compiler ja offenbar weis, wovon da die Rede ist (sonst hätte der sich ja bereits beschwert).

Ich komme jetzt wirklich nicht mehr weiter. Es ist doch hoffentlich nicht schon wieder so ein banaler Fehler?

Tschüß

Manni
 

cwriter

Erfahrenes Mitglied
Eigentlich schon ... so war wenigstens die Idee.
Ergänzung: Nachdem ich in der cpp-Datei ein include für den Header ergänzt hatte (ich hatte geglaubt, der Header in der Hauptheaderdatei des Projekts würde ausreichen) hat es soweit funktioniert. Doch jetzt beschwert der Linker sich über mehrfache Definitionen der Variable RGB_Led (der Instanz der Klasse direkt in der Klassendefinition hinten).
Genau darum nicht.
Wenn du es wirklich, wirklich brauchst:
C++:
//Headerdatei
extern RGB_LED_code RGB_LED;

//CPP-Datei
RGB_LED_code RGB_LED(45, 47, 32);
Aber das ist böse, denn normalerweise will man solche Bibliotheken weiternutzen können. Diese Zeile kannst du ja genausogut in die main.cpp schmeissen und von dort aus brauchen. Wenn du sie an andere Klassen übergeben willst, nutze Referenzen.

Ich wollte dann in den anderen Dateien, wo ich sie verwenden möchte, mit extern-Deklarationen arbeiten.
Ah, du hast es schon selbst gefunden.

Ich habe die Linker-Liste mit Absicht mit eingefügt (obwohl sie unübersichtlich lang ist), da sieht man dann, dass auch RGB_On_Adap.cpp als entsprechende Objekt-Datei mit in der Liste steht. Warum er das Symbol nicht erkennt, weis ich wieder mal nicht.
Das ist tatsächlich seltsam. Ist denn der Konstruktor noch in dieser Datei zu finden (ohne void)?

Vor allem, weil der Compiler ja offenbar weis, wovon da die Rede ist (sonst hätte der sich ja bereits beschwert).
Der Compiler sieht in solchen Fällen nur Deklarationen und sagt sich: "Ok, ich kenne die Definition noch nicht, also lasse ich das den Linker machen, denn ich werde ja wahrscheinlich die Funktion noch irgendwo finden und kompilieren." Dann findet er die Definition aber nirgends. Der Linker kommt und sieht, dass der Compiler Arbeit übrigliess, sucht selbst nochmals und findet nichts. Und dann beschwert er sich.

Da du ja mit extern herumgespielt hast, könnte das durchaus ein Problem sein.
Aber lass den Compiler erstmal alles neu erstellen, vielleicht hängt auch nur etwas.
Ansonsten würde ich darauf tippen, dass die Definition dieser Funktion fehlerhaft ist.
Oh und gib mal ein -Wall an den Compiler. Gibt es Warnungen? Wenn ja, welche?

Gruss
cwriter
 

ManniP

Grünschnabel
Hallo,
deshalb hatte ich die RGB_On_Adap.cpp-Datei erwähnt. Da steht nämlich die Definition der Funktion des Konstruktors drin. Außerdem: Wenn eine Funktion deklariert, aber nie definiert wird, beschwert sich der Compiler nach meiner Erfahrung eigentlich auch irgendwann. Das ist die Definition aus der erwähnten Datei
Code:
RGB_LED_Class::RGB_LED_Class(int Pin_Red = 32, int Pin_Green = 47, int Pin_Blue = 45)
{
   RGB_Pin[red] = Pin_Red;
   RGB_Pin[green] = Pin_Green;
   RGB_Pin[blue] = Pin_Blue;
   RGB_Init(red);
   RGB_Init(green);
   RGB_Init(blue);
   RGB_State[red] = Off;
   RGB_State[green] = Off;
   RGB_State[blue] = Off;
};

Die Vorgabewerte entstammen einem Experiment, den Konstruktor ohne Parameter aufzurufen. Die Funktion RGB_Init ist ebenfalls deklariert und definiert, sie setzt den Ausgang und stellt ihn auf HIGH, da die Ausgänge von PinMode auf LOW gestellt werden und die LED mit gem. Anode läuft, also für off HIGH braucht.

Deshalb bin ich ja so ratlos. Ich verstehe nicht, warum er die nicht findet.

Tschüß

Manni
 

cwriter

Erfahrenes Mitglied
deshalb hatte ich die RGB_On_Adap.cpp-Datei erwähnt. Da steht nämlich die Definition der Funktion des Konstruktors drin.
Weshalb?
Dein Output zeigt keinen Compileroutput (nur den Linkeroutput), weshalb nicht klar ist, ob die cpp-Datei selbst neukompiliert wurde...

Außerdem: Wenn eine Funktion deklariert, aber nie definiert wird, beschwert sich der Compiler nach meiner Erfahrung eigentlich auch irgendwann.
Deine Erfahrung ist falsch.
Der Linker beschwert sich, dem Compiler ist das sowas von Wurscht, ob der Code schon bekannt ist (Ausnahme: inline).

Hast du denn das Projekt neu erstellt? Keine Ahnung, wie das in der Arduino IDE heisst, normalerweise ist es "Bereinigen" oder "Neu erstellen".

Und ansonsten: Schau, ob du das Symbol findest (objdump/nm erforderlich; sollte in der Nähe des Compilers zu finden sein).
https://stackoverflow.com/questions/34732/how-do-i-list-the-symbols-in-a-so-file

Falls "nm -gC RGB_On_Adap.cpp.o" den Konstruktor nicht sieht, dann kann ihn der Linker auch nicht sehen.

Gruss
cwriter
 

ManniP

Grünschnabel
Hallo,
den nm gibt es hier, er heist allerdings avr-nm. Hoffentlich ist der Name der einzige Unterschied.

Tja, alle Hoffnung umsonst, es gibt weitere Unterschiede. Er gibt die Fehlermeldung :
plugin needed to handle lto object
00000001 C __gnu_lto_slim
00000001 C __gnu_lto_v1
aus. Das ist leider alles. Zu behaupten, ich könne was damit anfangen, wäre sicher übertrieben. Meine Ideen sind allerdings nicht wirklich ermutigend. Denn ich denke, dass sind Spezialdateien, zu denen ich weder den richtigen Namen noch die Quelle kenne. Hinzu kommt, dass die Sachen sicher nicht für Windows 7 taugen. Ich frage mich allerdings im Moment, warum dieses Programm (also nm) überhaupt dabei ist, wenn es nicht tut, wofür es nach meinem Eindruck einzig gut ist ... eine Objektdatei gewissermaßen nach bestimmten Kriterien 'auseinanderzunehmen'.

Allerdings: Die Dateien werden bei jedem Neustart der IDE auf jeden Fall neu kompiliert, denn beim Beenden derselben wird der Ordner mit ihnen gelöscht ... wie ich gerade auf die harte Tour erkennen musste. Dateien für AVR-µC sind nicht so umfangreich, dass man sich mit den ganzen Abhängigkeiten und so unbedingt befassen muss ... die Dinger sind kein Linux-Kern oder so. Also wird offenbar alles jedes Mal neu kompiliert. Da die IDE ja dafür da ist, die Experimente zu vereinfachen, ist man wohl davon ausgegangen, dass da viel experimentiert wird. Also auch viel geändert.

Nach dem Verfassen meiner Antwort hier habe ich den Rechner heruntergefahren und vorher die IDE beendet. Dann nochmal beim Verfassen dieser Antwort, um bei ALT-Tab nicht so viel Auswahl zu haben (unübersichtlich). Dann fand der nm allerdings die .o-Datei nicht mehr. So ergaben sich mindestens drei Compiler-Läufe seit der letzten Änderung, die die erwähnte Fehlermeldung verursachte. Wenn das Symbol also nicht drin ist, dann, weil da was faul ist, nicht, weil ich den Compiler nicht oft genug laufen ließ.

So, jetzt ist es vorbei. Das Problem war tatsächlich mal wieder banal: Es gibt in Marlin die Datei 'Configuration.h', in der alle Prä-Prozessor-Konstanten definiert werden, die den Übersetzungsvorgang steuern sollen ... je nachdem , was an dem Drucker vorhanden ist. Da hatte ich auch eine Konstante für diese LED eingebaut. Doch die war in der cpp-Datei nicht definiert und daher wurde deren Inhalt auch nicht korrekt übersetzt. Nachdem ich diese Konfigurationsdatei lade, klappt das Übersetzen .... und auch wieder nicht. Denn die Fehlermeldung: 'OUTPUT was not declared in this scope' ist natürlich bei einer Funktion wie 'pinMode' (Arduino-Funktion) völliger Schwachsinn. Sie steht in der Init-Funktion innerhalb der cpp-Datei. Wenn da dieses Schlüsselwort nicht hingehört, gehört es nirgendwo hin.

Da komme ich jetzt nicht mehr weiter und Ihr sicher auch nicht. Da muss ich zum Arduino-Forum wechseln.

Soweit also erstmal Danke. Die Tipps waren sehr hilfreich (vor allem als Denkanstöße).

Tschüß

Manni