std::set используется как статическая переменная-член
Я пытаюсь создать что-то вроде Enum в стиле Java, которое я называю флагом. Требования состоят в том, чтобы каждый флаг был статическим, поэтому на флаги можно ссылаться напрямую, каждый флаг хранит строку своего имени и весь набор, повторяемый и способствующий поиску.
Я использую шаблоны, так что каждый набор флагов хранится отдельно (что избавляет меня от необходимости явно размещать набор в каждом дочернем классе).
Я убежден, что это проблема инициации, потому что успех или неудача запуска программы зависит от имени файла объектного файла, который содержит объявления флагов (Ao segfaults, но Zo работает нормально.)
Кажется, что проблема заключается в статическом порядке инициализации, этот код прекрасно компилируется, но при запуске gdb выдает следующее:
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 ()
Мой код выглядит следующим образом:
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
Item.hpp
class ItemFlag: public Flag<ItemFlag>
{
public:
static const ItemFlag brick;
private:
ItemFlag(int ordinal, String name):
Flag<ItemFlag>(ordinal, name){}
};
Item.cpp
const ItemFlag ItemFlag::brick(1, "brick");
Мой первый пост, поэтому, пожалуйста, дайте мне знать, если у меня неправильное форматирование или он не указан. PS. Любопытно, что замена набора на вектор приводит к работающей программе, как будто у набора возникают проблемы с конкретной вставкой указателей. Чтобы проверить это, я заменил набор на набор int и попытался вставить 0 при инициализации класса, это также привело к той же ошибке.
3 ответа
Это очень легко может быть проблема порядка инициализации. Вам в основном нужно использовать какую-то ленивую инициализацию для набора, например
static std::set<Flag<FlagType> const*>& flagSet()
{
static std::set<Flag<FlagType> const*> theOneAndOnly;
return theOneAndOnly;
}
вместо вашей статической переменной.
И пока я в этом: это, вероятно, не очень хорошее использование шаблонов. Гораздо лучшим решением было бы сгенерировать код из файла с гораздо более простым форматом, примерно таким:
[EnumName]
constant_name_1
constant_name_2
и т. д. Вероятно, потребуется не более 10 строк AWK, Perl или Python (в зависимости от ваших предпочтений), чтобы проанализировать это и вывести для него как заголовок C++, так и исходный файл C++. Вам нужно только поддерживать простой формат.
Если конструктор вашего класса вставляет элементы в статический набор, то его деструктор должен удалить их. Вам также, вероятно, понадобится конструктор копирования и оператор присваивания. aso, с точки зрения стиля, защищенные данные обычно считаются плохой вещью.
Порядок статической инициализации между различными единицами перевода не гарантируется. Из свалки это похоже на ItemFlag
s создаются до того, как построено множество. Это делает вставку неудачной.
Изменение имени файла может повлиять на порядок файлов в процессе компоновки. Это объясняет, почему файл, начинающийся с буквы A, связан с ранними версиями.
Единственный способ сделать эту работу правильно, это иметь набор и ItemFlag
определены в том же файле.cpp. Тогда порядок всегда сверху вниз. Если они находятся в разных файлах, компоновщик решает порядок (в основном наугад).