4 Stimmen

std::set wird als statische Schablonenvariable verwendet

Ich versuche, so etwas wie ein Java-Stil Enum zu machen, die ich ein Flag nennen. Die Anforderungen sind, dass jede Flagge statisch ist, so dass Flaggen direkt referenzierbar sind, jede Flagge speichert die Zeichenfolge des Namens und die ganze Menge iterable und förderlich für Lookups.

Ich verwende Templating, so dass jeder Satz von Flaggen separat gespeichert wird (so sparen Sie mich von explizit einen Satz in jeder Kindklasse zu platzieren).

Ich bin davon überzeugt, dass es sich um ein Initiierungsproblem handelt, da der Erfolg oder Misserfolg der Ausführung des Programms vom Dateinamen der Objektdatei abhängt, die die Flag-Deklarationen enthält (A.o schlägt fehl, aber Z.o läuft einwandfrei).

Das Problem scheint eine der statischen Initialisierungsreihenfolge zu sein, dieser Code kompiliert völlig in Ordnung, aber wenn es ausgeführt wird, produziert gdb das folgende:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff751e0fa in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) ()
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/libstdc++.so.6
(gdb) bt
#0  0x00007ffff751e0fa in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) ()
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/libstdc++.so.6
#1  0x0000000000462669 in operator-- ()
at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_tree.h:199
#2  _M_insert_unique ()
at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_tree.h:1179
#3  insert () at /usr/lib/gcc/x86_64-pc-linux-gnu/4.4.5/include/g++-v4/bits/stl_set.h:411
#4  Flag () at include/../util/include/Flag.hpp:34
#5  ItemFlag () at include/Item.hpp:22
#6  __static_initialization_and_destruction_0 () at Item.cpp:15
#7  global constructors keyed to _ZN3code8ItemFlag5brickE() () at Item.cpp:86
#8  0x000000000046ac62 in ?? ()
#9  0x00007fffffffddc0 in ?? ()
#10 0x000000000046abb0 in ?? ()
#11 0x0000000000692c0a in ?? ()
#12 0x0000000000407693 in _init ()
#13 0x00007ffff7dded08 in ?? () from /usr/lib64/libboost_serialization-1_42.so.1.42.0
#14 0x000000000046abe7 in __libc_csu_init ()
#15 0x00007ffff6cd9b50 in __libc_start_main () from /lib64/libc.so.6
#16 0x0000000000408329 in _start ()

Mein Code lautet wie folgt:

template <class FlagType> class Flag
{
public:

    Flag(int ordinal, String name):
    ordinal(ordinal),
    name(name)
    {
        flagSet.insert(this);
    }

    inline bool operator==(const Flag<FlagType>& e) const
    {
                    //edited due to comment
        //if(this->ordinal == e.getOrdinal()) return true;
        //else return false;
                    return (this->ordinal == e.getOrdinal());

    }

    inline bool operator!=(const Flag<FlagType>& e) const
    {
        return !(*this==e);
    }

    static const std::set<const Flag<FlagType>*>& flagValues()
    {
        return flagSet;
    }

    const String& toString() const
    {
        return name;
    }

    const size_t& getOrdinal() const
    {
        return ordinal;
    }

    static int size()
    {
        return flagSet.size();
    }

    static const Flag<FlagType>& valueOf(const String& string)
    {
        typename std::set<const Flag<FlagType>*>::const_iterator i;
        for(i = flagSet.begin(); i != flagSet.end(); i++)
        {
            if((**i).toString().startsWith(string))
            {
                return **i;
            }
        }
        throw NotAFlagException();
    }

protected:

    static std::set<const Flag<FlagType>*> flagSet;

    size_t ordinal;
    String name;
    private:
            //added in response to comment to prevent copy and assignment, not compile tested
            Flag<FlagType>(const Flag<FlagType>&);
            Flag<FlagType>& operator=(const Flag<FlagType>&);
};

template <class FlagType> std::set<const Flag<FlagType>*> Flag<FlagType>::flagSet; //template

Artikel.hpp

    class ItemFlag: public Flag<ItemFlag>
{
public:

    static const ItemFlag brick;

private:

    ItemFlag(int ordinal, String name):
    Flag<ItemFlag>(ordinal, name){}
};

Element.cpp

const ItemFlag ItemFlag::brick(1, "brick");

Dies ist mein erster Beitrag, also lassen Sie mich bitte wissen, ob ich die Formatierung falsch gemacht habe oder unspezifisch war. PS. Seltsamerweise führt das Ersetzen von set durch vector zu einem funktionierenden Programm, als ob das set Probleme mit dem Einfügen der Zeiger hat. Um dies zu testen, habe ich das Set durch ein Set von int ersetzt und versucht, bei der Klasseninitialisierung 0 einzufügen, was ebenfalls zu demselben Fehler führte.

5voto

James Kanze Punkte 146902

Es könnte sehr leicht ein Problem mit der Reihenfolge der Initialisierung sein. Sie müssen grundsätzlich müssen Sie eine Art von träger Initialisierung für die Menge verwenden, z. B.

static std::set<Flag<FlagType> const*>& flagSet()
{
    static std::set<Flag<FlagType> const*> theOneAndOnly;
    return theOneAndOnly;
}

anstelle Ihrer statischen Variable.

Und wenn ich schon dabei bin: Das ist wahrscheinlich no eine gute Verwendung von Vorlagen. Eine viel bessere Lösung wäre es, den Code aus einer Datei zu generieren, die einem viel einfacheren Format zu generieren, etwa in der Art von:

[EnumName]
constant_name_1
constant_name_2

usw. Es würde wahrscheinlich nicht mehr als 10 Zeilen AWK, Perl oder Python (je nach Ihrem Geschmack) benötigen, um dies zu analysieren und sowohl einen C++ Header und eine C++-Quelldatei dafür auszugeben. Sie müssen dann nur noch das einfache Format pflegen.

0voto

Wenn der Konstruktor Ihrer Klasse Elemente in die statische Menge einfügt, sollte der Destruktor sie wieder entfernen. Sie brauchen wahrscheinlich auch einen Kopierkonstruktor und einen Zuweisungsoperator. Außerdem werden geschützte Daten normalerweise als schlechte Sache angesehen.

0voto

Bo Persson Punkte 88207

Die Reihenfolge der statischen Initialisierung zwischen verschiedenen Übersetzungseinheiten ist nicht garantiert. Aus dem Dump scheint es, dass die ItemFlag s werden erstellt, bevor die Menge konstruiert wird. Dadurch schlägt das Einfügen fehl.

Ändern des Namens der Datei nur puede die Reihenfolge der Datei im Verknüpfungsprozess beeinflussen. Das würde erklären, warum eine Datei, die mit einem A beginnt, früher eingebunden wird.

Die einzige Möglichkeit, dies zu bewerkstelligen, besteht darin, dass das Set und die ItemFlag s, die in der gleichen .cpp-Datei definiert sind. Dann ist die Reihenfolge immer von oben nach unten. Befinden sie sich in verschiedenen Dateien, entscheidet der Linker über die Reihenfolge (meist nach dem Zufallsprinzip).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X