Std :: set is used as a static template member variable

I am trying to do something like Java Enum, which I call a flag. The requirements are that each flag is static, so the flags have a direct link, each flag stores the name string and the whole set of iterations and helps the search.

I use templates, so that each set of flags is stored separately (this way, I do not want to explicitly place the set in each child class).

I am convinced that this is an initiation problem, because the success or failure of starting the program depends on the file name of the object containing the flag declarations (Ao segfaults, but Zo works fine.)

The problem, apparently, is one of the static orders of initialization, this code compiles fine, but when it starts, 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 have the wrong formatting or was non-specific. PS. with curiosity, replacing the set with vector results in the work program, as if the set had problems inserting pointers. To test this, I replaced the set with the set int and tried to insert 0 into the initialization of the class, this also led to the same error.

+4
source share
3 answers

This can be a very simple initialization order. You basically need to use some lazy initialization for typing, for example.

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

instead of your static variable.

And while I'm in: this is probably not a good use of templates. A much better solution would be to generate code from a file using a much simpler format, something like:

 [EnumName] constant_name_1 constant_name_2 

etc .. It will probably take no more than 10 lines of AWK, Perl or Python (to your liking) to parse this and output both the C ++ header and the C ++ source file. Then you only need to maintain a simple format.

+5
source

If your class constructor inserts elements into a static set, its destructor should remove them. You also probably need a copy operator and an assignment operator. aso, at the point of style, protected data is usually considered bad.

0
source

The order of static initialization between different translation units is not guaranteed. From the dump, it seems that ItemFlag are created before the set is created. This causes the insert to fail.

Changing the file name can only affect the file order during the linking process. This explains why a file starting with A is linked early.

The only way to do this job correctly is to set the bundle and ItemFlag defined in the same .cpp file. Then the order is always from top to bottom. If they are in different files, the linker decides the order (mostly randomly).

0
source

All Articles