multiple definition



  • frank schrieb:

    auf die Schnelle (mit extern) gings erstmal nicht (gleiche Fehlermeldung), ich muss mir dass mal genauer zu Gemüte führen.

    Wenn du die Definition nicht in eine .cpp-Datei verlagerst, nützt dir extern als explizite Deklaration ohne Definition wenig.

    frank schrieb:

    ich brauche das, weil das ein Kenner für diverse (static) funktionen ist, welche Auswege gäbe es denn außer einer globalen Variablen?

    Was meinst du genau mit dem "Kenner"?

    Im Allgemeinen entfallen viele globale Variablen, wenn du den Code strukturierst, in Klassen aufteilst und Aufgabenbereiche so lokal wie möglich hältst. Und statische Variablen sind nicht wirklich eine Alternative zu globalen.



  • Du willst vermutlich eine Konstante definieren. Wenn du es so machst, gibt's keine doppelte Definition:

    #ifndef LUA_CONTROLS_H
    #define LUA_CONTROLS_H
    #include ...
    ...
    const char* const WINDOW_CLASS = "WINDOW*";
    #endif
    

    Außerdem sollten per Konvention nur Makros (#define) komplett in Großbuchstaben geschrieben werden.



  • manni66 schrieb:

    Du willst vermutlich eine Konstante definieren. Wenn du es so machst, gibt's keine doppelte Definition:

    #ifndef LUA_CONTROLS_H
    #define LUA_CONTROLS_H
    #include ...
    ...
    const char* const WINDOW_CLASS = "WINDOW*";
    #endif
    

    Außerdem sollten per Konvention nur Makros (#define) komplett in Großbuchstaben geschrieben werden.

    finde ich nicht sinnvoll...
    was hat es den anwender (in diesem fall) zu interessieren, ob ein makro oder eine konstante ist?
    globale konstanten in caps zu schreiben ist imho kein fehler



  • Nexus schrieb:

    Wenn du die Definition nicht in eine .cpp-Datei verlagerst, nützt dir extern als explizite Deklaration ohne Definition wenig.

    verstehe ich das richtig, dass im Header nur extern char * WINDOW_CLASS; steht und die Zuweisung in der cxx (WINDOW_CLASS="WINDOW*")? geht vermutlich nicht mit const...und das werden die lua-funktionen nicht akzeptieren (wollen ziemlich sicher const)

    Nexus schrieb:

    Was meinst du genau mit dem "Kenner"?

    meine Vorlage (nur mit dem Unterschied, dass ich das für GTK schreibe).
    die Stellen mit dem WINDOW_CLASS sind aber gleich

    Nexus schrieb:

    Im Allgemeinen entfallen viele globale Variablen, wenn du den Code strukturierst, in Klassen aufteilst und Aufgabenbereiche so lokal wie möglich hältst. Und statische Variablen sind nicht wirklich eine Alternative zu globalen.

    bis auf die Lua-funktionen habe ich schon alles ausgelagert. die Lua-funktionsaufrufe, welche diese Konstante benötigen lassen sich aufgrund von Aufrufkonventionen (self-pointer bei klassen) bestimmt nicht in Klassen einbetten.
    mein Projekt liegt hier, auch wenn die letzten Änderungen mit dem WINDOW_CLASS noch nicht drin sind.

    gruß Frank



  • frank schrieb:

    LuaControls.o:(.data+0x0): multiple definition of `WINDOW_CLASS'
    lgui.o:(.data+0x0): first defined here
    collect2: ld returned 1 exit status
    
    #ifndef LUA_CONTROLS_H
    #define LUA_CONTROLS_H
    #include ...
    ...
    const char* WINDOW_CLASS = "WINDOW*";
    #endif
    

    wegen diesem Aufbau sollte der obenstehende Fehler doch eigentlich nicht kommen, oder?

    Woanders ist WINDOW_CLASS nicht definiert...es muss also an der headerdatei liegen.

    Doch doch. WINDOW_CLASS wird überall dort definiert, wo Du die Header-Datei einbindest. #include führt nur eine einfache Textersetzung durch. Du hast anscheinend diesen Header in mehr als einer Übersetzungseinheit eingebunden.

    Es gibt mehrere Möglichkeiten, wie man das jetzt lösen könnte.

    1. So hat WINDOW_CLASS eine externe Bindung. Du kannst WINDOW_CLASS eine interne Bindung verpassen. Damit hätte jede Übersetzungseinheit ihr eigenes WINDOW_CLASS. Das sieht dann so aus:

    static const char* WINDOW_CLASS = "WINDOW*";
    

    Beachte: Der Zeiger an sich ist nicht konstant. Nur das, worauf er zeigt ist const-qualifiziert. C++ verwendet für konstante globale Objekte automatisch eine interne Bindung. Wenn Du den Zeiger also nicht später ändern willst, kannst Du auch das hier schreiben:

    const char* [b]const[/b] WINDOW_CLASS = "WINDOW*";
    

    Hier ist der Zeiger an sich const und bekommt dadurch automatisch eine interne Bindung. Wir brauchen das static also nicht mehr, um die interne Bindung zu erzwingen.

    2. Alternativ kann man anonyme Namensräume nutzen, um einen ähnlichen Effekt zu erzielen. Jede Übersetzungeinheit bekommt damit auch ihre eigene WINDOW_CLASS-Variable, die dann aber trotzdem eine externe Bindung besitzt:

    namespace {
    const char* WINDOW_CLASS = "WINDOW*";
    }
    

    3. Als dritte Variante, die ich auch persönlich vorziehen würde, kannst Du im Header WINDOW_CLASS deklarieren und dann in einer cxx-Datei definieren:

    // header
    extern const char WINDOW_CLASS[];               // <- Deklaration
    
    // cxx-Datei
    extern const char WINDOW_CLASS[] = "WINDOW*";   // <- Definition
    }
    

    Hier habe ich mir das mit dem Zeiger auch mal gespart. Im Header wird WINDOW_CLASS als Array unbekannter Länge (unvollständiger Typ) mit externer Bindung deklariert und im cxx-File wird es definiert. Jede Übersetzungeinheit, die den Header einbindet, wird sich garantiert auf dasselbe Array beziehen.

    Stichwörter für die weitere Recherche:
    - separate compilation
    - linkage
    - one definition rule

    kk



  • unskilled schrieb:

    finde ich nicht sinnvoll...
    was hat es den anwender (in diesem fall) zu interessieren, ob ein makro oder eine konstante ist?
    globale konstanten in caps zu schreiben ist imho kein fehler

    Was hat das mit irgend einem Anwender zu tun? Du hast das Problem nicht verstanden.

    Was kommt dabei raus?

    #define min
    
    int min();
    

    Das define kann in irgend einem Header aus einem beliebigen Framework stehen.



  • manni66 schrieb:

    Was kommt dabei raus?

    #define min
    
    int min();
    

    Das define kann in irgend einem Header aus einem beliebigen Framework stehen.

    andersrum wäre es richtiger..
    also wen schon
    int MIN(); -> selbst schuld



  • unskilled schrieb:

    also wen schon
    int MIN(); -> selbst schuld

    Nun, das ist genau das, von dem du sagst "finde ich nicht sinnvoll... ".

    => selbst schuld



  • ists gar nicht
    ich habe nicht gesagt, dass man makros nicht grundsätzlich im upper-case schreiben sollte.

    ich habe lediglich gesagt, dass man konstanten ebenso in caps schreiben kann.
    oder merkst du als anwender einen unterschied zwischen

    #define MAX_SIZE 512
    //und
    const std::size_t MAX_SIZE = 512;
    

    ich glaube nicht...

    bb



  • Nebenbei: Makros mit Allerweltsnamen wie MIN zu benennen, ist ohnehin ein Verbrechen.



  • unskilled schrieb:

    oder merkst du als anwender einen unterschied zwischen

    #define MAX_SIZE 512
    //und
    const std::size_t MAX_SIZE = 512;
    

    *haarspalt*

    struct foo {
      int MAX_SIZE; // int 512 ?
    };
    
    int main() {
      cout << sizeof(MAX_SIZE) << '\n'; // sizeof(int) oder sizeof(std::size_t) ?
      cout << &MAX_SIZE << '\n';        // hat MAX_SIZE eine Adresse ?
    }
    

Anmelden zum Antworten