Registering call to object factory causes segfault

117 Views Asked by At

I'm trying to build a simple object factory in c++. So far i have this code:

The Factory:

class Factory{
    public:
        TCreateMethod = std::function<Simulation*(std::string)>;
    public:
        Factory() = delete;

        static bool Register(const std::string name, TCreateMethod funcCreate){
            const auto [it, success] = s_methods.insert({name, funcCreate});
            return success;
        };

        static Simulation* Create(const std::string name, const std::string ini_file){
            if (auto it = s_methods.find(name); it != s_methods.end()){
                return it->second(ini_file);
            }
            return nullptr;
        };

    private:
        static std::map<std::string, TCreateMethod> s_methods;
};

std::map<std::string, Factory::TCreateMethod> Factory::s_methods;

The Object:

class SimulationStuff : public Simulation{
    public:
        SimulationStuff(std::string ini_file);

        static Simulation* createMethod(std::string ini_file){
            return new SimulationStuff(ini_file);
        };

        static std::string getFactoryName(){
            return "SimulationStuff";
        };

    private
        static bool s_registerd;
};

bool SimulationStuff::s_registered = Factory::Register(SimulationStuff::getFactoryName(), SimulationStuff::createMethod);

Backtrace of the segv:

#0  0x00007ffff75ae44a in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00005555556192a1 in std::_Rb_tree_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >::operator--() (this=0x7fffffffd820)
    at /usr/include/c++/8/bits/stl_tree.h:302
#2  0x0000555555618e36 in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > > >::_M_get_insert_unique_pos(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (this=0x5555557bc180 <Factory::s_methods[abi:cxx11]>, __k="SimulationStuff")
    at /usr/include/c++/8/bits/stl_tree.h:2063
#3  0x0000555555618b05 in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > > >::_M_insert_unique<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >&&) (this=0x5555557bc180 <Factory::s_methods[abi:cxx11]>, __v=...)
    at /usr/include/c++/8/bits/stl_tree.h:2106
#4  0x00005555556188b2 in std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > > >::insert(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >&&) (
    this=0x5555557bc180 Factory::s_methods[abi:cxx11]>, __x=...) at /usr/include/c++/8/bits/stl_map.h:809
#5  0x00005555556184ab in Factory::Register(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::function<Simulation* (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>) (name="SimulationStuff", funcCreate=...)
    at src/factory.cpp:13
#6  0x000055555560ca19 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at src/simulation_stuff.cpp:24
#7  0x000055555560ca7e in _GLOBAL__sub_I__ZN28SimulationStuff12s_registeredE () at src/simulation_stuff.cpp:73
#8  0x00005555556c3d75 in __libc_csu_init ()
#9  0x00007ffff71aa02a in __libc_start_main (main=0x55555569f7eb <main(int, char**)>, argc=1, argv=0x7fffffffdb88, init=0x5555556c3d30 <__libc_csu_init>, 
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdb78) at ../csu/libc-start.c:264
#10 0x00005555555a5a6a in _start ()

The Problem I have is that I'm unable to understand why the segv is happening as the s_methods map should be 0 initialized at compiletime and s_registered should be initialized at the start of the program.

1

There are 1 best solutions below

2
On BEST ANSWER

You've run afoul of the static initialization order fiasco. The order of initialization of objects with static lifetime (such as globals and static class members) that are defined in different compilation units is unspecified. It's entirely possible for SimulationStuff::s_registered to be initialized before Factory::s_methods. If that happens then your std::map isn't initialized when you attempt to insert into it.

To work around this, construct your registry map on first use. i.e.:

class Factory {
        // ...
        static std::map<std::string, TCreateMethod>& getMethods() {
            static std::map<std::string, TCreateMehtod> methods;
            return methods;
        }

        static bool Register(const std::string name, TCreateMethod funcCreate){
            const auto [it, success] = getMethods().insert({name, funcCreate});
            return success;
        };
        // ...
};