std :: set用作静态模板化成员变量

时间:2011-05-09 13:49:09

标签: c++ templates enums static-initialization

我正在尝试制作类似Java风格的Enum,我称之为旗帜。要求是每个标志都是静态的,因此标志可以直接引用,每个标志存储其名称的字符串,整个集合可迭代并有助于查找。

我正在使用模板化,以便每组标志分开存储(从而使我不必在每个子类中明确地放置一个集合)。

我确信这是一个启动问题,因为运行程序的成功或失败取决于包含标志声明的目标文件的文件名(A.o segfaults但Z.o运行正常。)

问题似乎是静态初始化顺序之一,此代码编译完全正常,但在运行时,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。奇怪的是,用向量替换set会产生一个工作程序,好像该集合在插入指针时遇到了麻烦。为了测试这个,我用一组int替换了set,​​并尝试在类初始化时插入0,这也导致了同样的错误。

3 个答案:

答案 0 :(得分:5)

很容易就是初始化问题的顺序。你基本上 需要对集合使用某种延迟初始化,例如

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 ++源文件。然后你只需要保持 简单的格式。

答案 1 :(得分:0)

如果类的构造函数将项插入到静态集中,那么它的析构函数应该删除它们。您可能还需要一个复制构造函数和赋值运算符。 aso,在风格上,受保护的数据通常被认为是一件坏事。

答案 2 :(得分:0)

不保证不同翻译单元之间静态初始化的顺序。从转储开始,似乎在构造集合之前创建了ItemFlag。这使插入失败。

更改文件的名称可能会影响链接过程中文件的顺序。这可以解释为什么以A开头的文件会在早期链接。

使这项工作正常运行的唯一方法是在同一个.cpp文件中定义集合和ItemFlag。然后订单总是从上到下。如果它们位于不同的文件中,则链接器决定顺序(主要是随机的)。