merkwürdiger multiple definition of...first defined here mit gnu



  • Hi,
    ich kann mir vorstellen dieser Fehler kam schon oft. Nun ich hab schon alles ausprobiert, also ist es entweder ein unentdeckter Fehler im Code (Ich habe aber den Textdeditor Notepad nach bestimmten Wörtern absuchen lassen) oder...
    ich habe insgesamt acht Dateien, 4 header und 4 cpp. Eine main-Methode ist implementiert, damit alles compiliert werden kann, aber das Haupt-file soll später noch woanders sein:

    #ifndef Fortrancompilersystemsimulator_arithmetischeAusdruecke
    #define Fortrancompilersystemsimulator_arithmetischeAusdruecke
    #include"Variabeln.hpp"
    #include<exception>
    #include<string>
    #include<sstream>
    extern "C" {
    	#include<quadmath.h>
    }
    #include<cctype>
    #include<iostream>
    namespace arithmetischeAusdruecke{
    	__float128 berechnearithmetischenAusdruck(std::ostringstream*Ausgabestream,const std::string&Ausdruck);
    	__float128 berechnemitStringoperator(const __float128&n1,const std::string&s,const __float128&n2);
    	bool isteseinOperator(std::string s);
    }
    #endif
    

    und

    #include"arithmetischeAusdruecke.hpp"
    
    __float128 arithmetischeAusdruecke::berechnearithmetischenAusdruck(std::ostringstream*Ausgabestream,const std::string&Ausdruck){
    	bool Kommazahlen{true};
    	if(Ausdruck.find(".")==std::string::npos)
    	Kommazahlen=false;
    	int i=Ausdruck.size();
    		char a=Ausdruck[i];
    		if(std::toupper(a)=='F'){
    
    		}
    		else if(std::isalpha(a)){
    			std::string Variabel=Variabeln::extrahiereVariabelvonhinten(Ausgabestream,Ausdruck);
    			if(Ausgabestream->str()=="to many characters")
    				;
    			else if(Variabeln::Quadlevariabeln.istesvorhanden(Variabel)
    						||Variabeln::Integervariabeln.istesvorhanden(Variabel))
    				(*Ausgabestream)<<"Variabel nicht definiert";
    			else if(true);
    			
    				
    		}
    		else if(std::isdigit(a)||a=='.'){
    			std::string Zahlstring{""};
    			bool Punktbesetzt{false};
    			if(Kommazahlen&&Ausdruck[i]=='.'){
    				Punktbesetzt=true;
    				Zahlstring.append(Ausdruck.substr(i,1));
    				i--;
    			}
    			while(std::isdigit(Ausdruck[i])){
    				Zahlstring.insert(0,Ausdruck.substr(i,1));
    				i--;
    			}
    			if(!(Ausdruck[i]=='.')&&Kommazahlen&&!Punktbesetzt){
    				Zahlstring.insert(0,Ausdruck.substr(i));
    				i--;
    			}
    			return arithmetischeAusdruecke::berechnearithmetischenAusdruck(Ausgabestream,Ausdruck.substr(0,i));
    		}
    		else if(std::isblank(a))
    
    		;
    		else if(a=='(')
    
    		;
    		else if(a==')')
    
    		;
    		else if(a=='+'||a=='-'||a=='*'||a=='/'||a=='*'&&Ausdruck[i+1]=='*')
    
    		;
    		else if(a==',')
    
    		;
    		else if(a=='.')
    
    		;
    		else
    		*Ausgabestream<<"error";
    	return 0;
    
    }
    __float128 arithmetischeAusdruecke::berechnemitStringoperator(const __float128&n1,const std::string&s,const __float128&n2){
    	if(s=="+")return n1+n2;
    	else if(s=="-")return n1-n2;
    	else if(s=="*")return n1*n2;
    	else if(s=="/")return n1/n2;
    	else if(s=="**")return powq(n1,n2);
    	else return FLT128_MAX;
    }
    bool arithmetischeAusdruecke::isteseinOperator(std::string s){
    return s=="+"||s=="-"||s=="*"||s=="/"||s=="**";
    }
    
    
    int main(int argc,char*argv[]){
    	std::ostringstream*Ausgabestream=new std::ostringstream{"abc"};
    	arithmetischeAusdruecke::berechnearithmetischenAusdruck(Ausgabestream,"wird 1. mal ausgegeben");
    	std::cout<<Ausgabestream->str();
    	return 0;
    }
    

    und

    #ifndef Fortrancompilersystemsimulator_Variabeln
    #define Fortrancompilersystemsimulator_Variabeln
    #include<string>
    #include<unordered_map>
    #include<cctype>
    #include<sstream>
    #include<exception>
    extern "C" {
    	#include<quadmath.h>
    }
    namespace Variabeln{
    	template<typename t>class Fortranvariabeln{
    		typename std::unordered_map<std::string,t>Variabeln{};
    		public:
    			erstelleVariable(typename std::string Name,t Wert){
    				if(6<Name.size())
    					throw std::domain_error();
    				return Variabeln[Name]=Wert;
    			}
    			typename std::unordered_map<std::string,t>::iterator
    						find(const typename std::unordered_map<std::string,t>::key_type k){
    				return Variabeln.find(k);
    			}
    			typename std::unordered_map<std::string,t>::const_iterator
    						find(const typename std::unordered_map<std::string,t>::key_type k)const{
    				return Variabeln.find(k);
    			}
    			typename std::unordered_map<std::string,t>::mapped_type
    						&operator[](const typename std::unordered_map<std::string,t>::key_type k){
    				if(!istesvorhanden(k))
    					throw std::domain_error();
    				return Variabeln[k];
    					
    			}
    			bool istesvorhanden(typename std::unordered_map<std::string,t>::key_type k){
    				return!(Variabeln.find(k)==Variabeln.end());
    			}
    	};
    	Fortranvariabeln<__float128>Quadlevariabeln;
    	Fortranvariabeln<int>Integervariabeln;
    	std::string extrahiereVariabelvonhinten(std::ostringstream*Ausgabestream,std::string Variabel);
    }
    #endif
    

    sowie

    #include"Variabeln.hpp"
    
    std::string Variabeln::extrahiereVariabelvonhinten(std::ostringstream*Ausgabestream,std::string Variabel){
    	std::string Ausgabe{""};
    	for(int i;0<Variabel.size()&&isalpha(Variabel[i]);i--)
    		Ausgabe.append(std::string(1,Variabel[i]));
    	if(6<Ausgabe.size())
    		(*Ausgabestream)<<"to many characters";
    	return Ausgabe;
    }
    

    Die anderen 2 (4) Dateien kompiliere ich zwar mit, sie werden allerdings nicht von den anderen Dateien eingebunden.
    Der Befehl in der CMD ist:

    g++ -o Programm Betriebssystem.cpp Tape.cpp Variabeln.cpp arithmetischeAusdruecke.cpp -lquadmath
    

    Die Ausgabe ist dann:

    D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\*usw*\AppData\Local\Temp\ccF8Wugi.o:arithmetischeAusdruecke.cpp:(.bss+0x0): multiple definition of `Variabeln::Quadlevariabeln'; C:\*usw*\AppData\Local\Temp\cc0XJGv0.o:Variabeln.cpp:(.bss+0x0): first defined here
    D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\*usw*\AppData\Local\Temp\ccF8Wugi.o:arithmetischeAusdruecke.cpp:(.bss+0x40): multiple definition of `Variabeln::Integervariabeln'; C:\*usw*\AppData\Local\Temp\cc0XJGv0.o:Variabeln.cpp:(.bss+0x40): first defined here
    collect2.exe: error: ld returned 1 exit status
    

    Wie gesagt hab ich mit Notepad nach allen Integer- und Quadlevariabeln ohne eine weitere Definition zu finden suchen lassen. Auch ein cpp wurde nirgendwo gefunden (Allerdings hab ich die Variabeln eh im Header definiert). Beim Einbinden des Headers sollten mich die Präprozessor-Direktiven vor mehrfacher Definition retten. Ein extern wäre also nicht nötig (Außerdem doof, wenn ich die Dateien falschherum einbinde und der Variable dann irgendeinen Wert zuweise). ich hoffe jemand kennt die Antwort dazu. Vielen Dank schonmal!



  • Du hast in
    Variabeln.hpp
    2 Variablendefinitionen am Ende:

    Fortranvariabeln<__float128>Quadlevariabeln;
    Fortranvariabeln<int>Integervariabeln;
    

    Sobald Du

    #include "Variabeln.hpp"
    

    in mehr als einer Übersetzungseinheit stehen hast, kommt es zur Mehrfachdefinition.



  • [Belli] =>

    • Also besser keine globalen variablen
    • Oder: extern Type bla; (wenns unbedingt sein muss, idc)
      und dann in einer source definieren.


  • Mist ich krieg das nicht angezeigt wenn jemand antwortet.

    Habe ich nicht genau für diesen Fall die Präprozesserdirektiven? Bei Mehrfacheinbindung wird das vom Präprozesser doch direkt gelöscht, weil die if-Bedingung nicht greift oder nicht?

    Außerdem würde ich die Deklaration gerne im Headerfile kapseln noch wird der Variable erneut Speicherplatz zugewiesen, denn ich verwende sie ja als wäre alles eine Datei.



  • Nein, die Include-Guards verhindern nur, daß dieser Header nicht mehrfach innerhalb einer Übersetzungseinheit eingebunden wird. Bei verschiedenen Übersetzungseinheiten (d.h. mehreren Source-Dateien) wird dieser Header jedesmal wieder neu mit einkompiliert.

    Die Definition einer globalen Variablen muß daher in genau einer Übersetzungseinheit stehen.



  • @Ichwerdennsonst sagte in merkwürdiger multiple definition of...first defined here mit gnu:

    Habe ich nicht genau für diesen Fall die Präprozesserdirektiven?

    Jein bzw. nein.

    Präprozessordirektiven können sich immer nur auf einen Compilevorgang beziehen - den, der gerade aktuell läuft.

    Wenn du also z.B. erst A.cpp kompilierst und dort dein variablen.h eingebunden ist, wir das Ergebnis (die .o-Datei) die Variable enthalten.
    Dann kompilierst du B.cpp, die auch variablen.h einbindet. Auch hier wird das Kompilat die Variablen enthalten.
    Dann kommt der Linker und linkt die beiden Ergebnisse zusammen -> geht nicht, weil die Variable 2x da ist.

    Die Includeguards schützen dich davor, dass du in einem (einzigen) Kompiliervorgang einen Header doppelt einbindest.

    Du könntest die Variablen in ein "variablen.cpp" verschieben und in dem Header nur vorwärtsdeklarieren. Oder viel besser: gar keine globale Variablen verwenden!



  • @Ichwerdennsonst Wenn du Variablen in Header-Files definieren willst, gilt das selbe wie wenn du Funktionen in Header-Files definieren willst: du solltest sie inline machen. Sonst passiert genau das was du hier siehst: du bekommst Linker-Fehler.



  • @Th69 sagte in merkwürdiger multiple definition of...first defined here mit gnu:

    Nein, die Include-Guards verhindern nur, daß dieser Header nicht mehrfach innerhalb einer Übersetzungseinheit eingebunden wird. Bei verschiedenen Übersetzungseinheiten (d.h. mehreren Source-Dateien) wird dieser Header jedesmal wieder neu mit einkompiliert.

    Die Definition einer globalen Variablen muß daher in genau einer Übersetzungseinheit stehen.

    Achso
    Danke



  • So wie @hustbaer geschrieben hat, kannst du seit C++17 einfach inline dafür benutzen:

    inline Fortranvariabeln<__float128> Quadlevariabeln;
    inline Fortranvariabeln<int> Integervariabeln;
    

    (statt extern und Definition in einer Source-Datei)

    Evtl. mußt du dann explizit -std=C++17 als Compilerparameter angeben.

    PS: Du hast einige Rechtschreibfehler bei dir drin:

    • Variabel -> Variable
    • "to many characters" -> "too many characters"

    Auch die Mischung von deutsch und englisch ist nicht so leserlich, besser gleich alle Bezeichner in englisch halten.

    Und viel Erfolg mit deinem Fortrancompilersystemsimulator.



  • Diese inline-Variablen sind ja schön und gut, aber ich möchte nochmal betonen, dass die bessere Lösung ist, keine globalen Variablen zu benutzen. Man läuft immer nur in Probleme rein. Es fängt schon mit der Frage an, wann die Variablen eigentlich erzeugt/initialisiert werden.

    Was hindert dich daran, diese Variablen nicht-global zu erzeugen? https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-global


Anmelden zum Antworten