Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Segfault when adding an element to a std::map

Tags:

c++

stdmap

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

like image 917
Alexis Avatar asked Dec 19 '22 09:12

Alexis


1 Answers

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.

like image 180
Mike Seymour Avatar answered Feb 10 '23 19:02

Mike Seymour