std::set 用作静态模板化成员变量
我正在尝试制作类似 Java 风格的 Enum 的东西,我将其称为标志。要求是每个标志都是静态的,因此标志可以直接引用,每个标志存储其名称的字符串,并且整个集合可迭代并有利于查找。
我正在使用模板,以便每组标志都单独存储(从而使我不必在每个子类中显式放置一组标志)。
我确信这是一个启动问题,因为运行程序的成功或失败取决于包含标志声明的目标文件的文件名(Ao 段错误,但 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");
我的第一篇文章,所以如果我的格式错误或不具体,请告诉我。 附言。奇怪的是,用向量替换集合会产生一个工作程序,就好像集合在专门插入指针时遇到麻烦一样。为了测试这一点,我用一组 int 替换了该集合,并尝试在类初始化时插入 0,这也导致了相同的错误。
I am trying to make something like a Java style Enum, which I'm calling a flag. The requirements are that each flag is static so flags are directly referencable, each flag storing the string of it's name and the whole set iterable and conducive to lookups.
I am using templating so that each set of flags is stored separately (thus saving me from having to explicitly place a set in each child class).
I am convinced this is an initiation problem because the success or failure of running the program depends on the filename of the object file which contains the flag declarations (A.o segfaults but Z.o runs fine.)
The problem seems to be one of static initialization order, this code compiles perfectly fine but when it is run, gdb produces the following:
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 ()
My code is as follows:
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");
My first post, so please let me know if I've got the formatting wrong or have been unspecific.
PS. curiously, replacing set with vector results in a working program, as if the set is having trouble with inserting the pointers specifically. To test this I replaced the set with a set of int and tried to insert 0 on class initialization, this also resulted in the same error.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这很可能是初始化顺序问题。你基本上
需要对集合使用某种延迟初始化,例如
代替静态变量。
当我这样做时:这可能不是模板的良好用途。
更好的解决方案是从文件生成代码
一种更简单的格式,类似于:
等等。它可能需要不超过 10 行 AWK、Perl 或
Python(根据你的口味)解析它并输出 C++
头文件和它的 C++ 源文件。然后你只需要维护
简单的格式。
It could very easily be an order of initialization issue. You basically
need to use some sort of lazy initialization for the set, e.g.
instead of your static variable.
And while I'm at it: this is probably not a good use of templates.
A much better solution would be to generate the code from a file with
a much simpler format, something along the lines of:
etc. It would probably take no more than about 10 lines of AWK, Perl or
Python (according to your tastes) to parse this and output both a C++
header and a C++ source file for it. You then only have to maintain the
simple format.
如果类的构造函数将项插入静态集中,则其析构函数应删除它们。您可能还需要复制构造函数和赋值运算符。 aso,就风格而言,受保护的数据通常被认为是一件坏事。
If your class's constructor inserts items into the static set then its destructor should remove them. You also probably need a copy constructor and assignment operator. aso, on a point of style, protected data is normally considered A Bad Thing.
不保证不同翻译单元之间静态初始化的顺序。从转储来看,
ItemFlag
似乎是在构造集合之前创建的。这使得插入失败。仅更改文件名可能会影响链接过程中文件的顺序。这可以解释为什么以 A 开头的文件会提前链接。
使其正常工作的唯一方法是在同一个 .cpp 文件中定义该集合和
ItemFlag
。那么顺序总是从上到下。如果它们位于不同的文件中,则链接器决定顺序(大多数是随机的)。The order of static initialization between different translation units are not guaranteed. From the dump it seems like the
ItemFlag
s are created before the set is constructed. That makes the insert fail.Changing the name of the file just might affect the order of the file in the linking process. That would explain why a file starting with an A is linked in early.
The only way to make this work properly is to have the set and the
ItemFlag
s defined in the same .cpp file. Then the order is always top-to-bottom. If they are in different files, the linker decides the order (mostly at random).