Alexis
Alexis

Reputation: 2179

Segfault when adding an element to a std::map

I have something strange happening when I try to insert one element in a map

main.cpp

S3Wrapper wrapper = S3Wrapper::getS3Wrapper();
int main(){
    return 0;
}

So basically only the singleton is called for S3Wrapper

S3Wrapper.hpp

class S3Wrapper
{
    S3Wrapper(std::string bucketName);
public:
    ~S3Wrapper();
    static S3Wrapper   &getS3Wrapper(std::string name = BUCKET_NAME);
}

and S3Wrapper.cpp

static std::map<std::string, S3Wrapper*> _wrapperMap;
S3Wrapper&  S3Wrapper::getS3Wrapper(std::string name)
{
    auto it = _wrapperMap.find(name);
    if (it == _wrapperMap.end())
    {
        auto t = new S3Wrapper(name);
        _wrapperMap[name] = t;
        return *(_wrapperMap[name]);
    }
    return *(it->second);
}

When I compile I don't have any error/warning but then the program segfault:

g++ main.cpp S3Wrapper.cpp -std=c++0x -ls3 -g3

result of gdb

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7937c4a in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0  0x00007ffff7937c4a in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x0000000000406295 in std::_Rb_tree_iterator<std::pair<std::string const, S3Wrapper*> >::operator-- (this=0x7fffffffd8b0) at /usr/include/c++/4.8/bits/stl_tree.h:204
#2  0x00000000004061f7 in std::_Rb_tree<std::string, std::pair<std::string const, S3Wrapper*>, std::_Select1st<std::pair<std::string const, S3Wrapper*> >, std::less<std::string>, std::allocator<std::pair<std::string const, S3Wrapper*> > >::_M_get_insert_unique_pos (this=0x60b580 <_wrapperMap>, __k=...) at /usr/include/c++/4.8/bits/stl_tree.h:1333
#3  0x00000000004058f5 in std::_Rb_tree<std::string, std::pair<std::string const, S3Wrapper*>, std::_Select1st<std::pair<std::string const, S3Wrapper*> >, std::less<std::string>, std::allocator<std::pair<std::string const, S3Wrapper*> > >::_M_get_insert_hint_unique_pos (this=0x60b580 <_wrapperMap>, __position=..., __k=...) at /usr/include/c++/4.8/bits/stl_tree.h:1425
#4  0x000000000040511c in std::_Rb_tree<std::string, std::pair<std::string const, S3Wrapper*>, std::_Select1st<std::pair<std::string const, S3Wrapper*> >, std::less<std::string>, std::allocator<std::pair<std::string const, S3Wrapper*> > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<std::string const&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<std::string const, S3Wrapper*> >, std::piecewise_construct_t const&, std::tuple<std::string const&>&&, std::tuple<>&&) (this=0x60b580 <_wrapperMap>, __pos=...) at /usr/include/c++/4.8/bits/stl_tree.h:1673
#5  0x0000000000404d24 in std::map<std::string, S3Wrapper*, std::less<std::string>, std::allocator<std::pair<std::string const, S3Wrapper*> > >::operator[] (this=0x60b580 <_wrapperMap>, __k=...)
    at /usr/include/c++/4.8/bits/stl_map.h:465
#6  0x0000000000404945 in S3Wrapper::getS3Wrapper (name=...) at S3Wrapper.cpp:55
#7  0x00000000004021e3 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at main.cpp:8
#8  0x00000000004022ce in _GLOBAL__sub_I_wrapper () at main.cpp:41
#9  0x0000000000406b7d in __libc_csu_init ()
#10 0x00007ffff7308e55 in __libc_start_main (main=0x401edc <main()>, argc=1, argv=0x7fffffffdc28, init=0x406b30 <__libc_csu_init>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffdc18) at libc-start.c:246
#11 0x0000000000401b79 in _start ()

line55: _wrapperMap[name] = t;

If I insert t in a vector I don't have any problem

Upvotes: 4

Views: 7130

Answers (2)

Christophe Moine
Christophe Moine

Reputation: 1076

Thx Mike very much for the explanation that helped me a lot.

I could not easily get rid of static initialization in my case: so I simply changed the order of object files being linked in the Makefile and that fixed my problem! Not sure if the order for the linker is something we can rely on (I am using GCC)

Upvotes: 0

Mike Seymour
Mike Seymour

Reputation: 254431

This is the static initialisation fiasco: static variables defined in different translation units are initialised in an unspecified order, so there's no guarantee that _wrapperMap is initialised before it's needed by the initialisation of wrapper.

The best solution is to avoid static/global variables. They usually cause nothing but trouble.

If you really want global state, a safer option is to use a local static variable:

std::map<std::string, S3Wrapper*> & wrapperMap() {
    static std::map<std::string, S3Wrapper*> map;
    return map;
}

This is guaranteed to be initialised the first time the function is called, so there's no danger of using the map before initialisation. You may still have issues when static variables are destroyed at the end of the program.

Note that _wrapperMap is reserved (in the global namespace) so you should choose a name without a leading underscore, or put it in a namespace.

Upvotes: 12

Related Questions