views:

131

answers:

2

Hello,

I've been having some difficulties with diagnosing a segmentation fault resulting from, or at least I think resulting from a template'd static class (see original post here http://stackoverflow.com/questions/3200360/help-understanding-segfault-with-stdmap-boostunordered-map).

Since posting that I found some other bizarre behavior in my program. I've never encountered anything like this, and I think I must be unaware of some subtle detail on how templates work, resulting in me doing something wrong somewhere.

I've tried mindlessly refactoring things to try and make the problem disappear but it still persists.

The most perplexing symptom is this: in one source file (Menu.cpp) I have the following three calls:

Font::init();
// later
g_font = Font::get("Mono.ttf");
// later
Font::release();

In the other source file (Game.cpp) I have nearly the same three lines. Nearly all the code in Menu.cpp is executed before anything in Game.cpp is executed.

Now everything is fine with this. However, if I simply comment out

g_font = Font::get("Arial.ttf");

in Game.cpp the program encounters a segmentation fault at g_font = ... in Menu.cpp (note: everything in Menu.cpp and Game.cpp have their own namespace). But NO code has even been executed in Game.cpp at this point!

Now if I link Menu.o before Game.o the problem goes away (but other problem exist elsewhere). What am I not understanding here?

I've used a debugger to step through the program to see what's going on. When the g_font line is commented out in Game.cpp, a boost container (unordered_map) in Font's base class Resource somehow doesn't get initialized correctly. Specifically buckets_ and size_ are uninitialized and result in erratic behavior. I've tried using std::map and similar problems exist.

Here is the full listing for Font.hpp

#ifndef __Font_hpp__
#define __Font_hpp__

#include <string>
#include "Vector.hpp"
#include "Resource.hpp"

class FTPixmapFont;

/*!
 * Adapter class for FTGL's FTPixmapFont
 */
class Font : public Resource<Font>
{
public:
    Font(const std::string& fileName);
 ~Font();

 void render(const std::string& str, const Vector& pos, int ptSize=14) const;

private:
 FTPixmapFont *m_font;
};

#endif // __Font_hpp__

Here is the full listing for Resource.hpp (it potentially leaks memory right now on release(), I was originally using boost::shared_ptr in the container but switched to raw pointers thinking it would fix everything, doh).

#ifndef __Resource_hpp__
#define __Resource_hpp__

#include <string>
#include <map>
#include <boost/unordered_map.hpp>
#include <boost/utility.hpp>
#include "debug.hpp"
#include "assert.hpp"

#define MAP_TYPE boost::unordered_map

/*!
 * Resource base class.
 */
template <class T>
class Resource : public boost::noncopyable
{
public:
 static void init(const std::string& dir="data")
 {
  ASSERT(!c_init);
  if (*dir.rbegin() == '/') {
   c_dataDirectory = dir;
  } else {
   c_dataDirectory = dir + '/';
  }
  c_init = true;
 }

 static void release()
 {
  ASSERT(c_init);
  c_dataDirectory.clear();
  c_resources.clear();
  c_init = false;
 }

 static const T *get(const std::string& fileName)
 {
  T *resource = NULL;

  typename MAP_TYPE<std::string, T*>::const_iterator itr = c_resources.find(fileName);
  if (itr == c_resources.end()) {
   resource = new T(c_dataDirectory + fileName);
   c_resources.insert(std::pair<std::string, T*>(fileName, resource));
  } else {
   resource = itr->second;
  }

  return resource;
 }

private:
 static bool c_init;
 static std::string c_dataDirectory;
 static typename MAP_TYPE<std::string, T*> c_resources;
};

template <class T> bool Resource<T>::c_init = false;
template <class T> std::string Resource<T>::c_dataDirectory;
template <class T> MAP_TYPE<std::string, T*> Resource<T>::c_resources;

#endif // __Resource_hpp__

Here is some output (stack trace) from gdb with MAP_TYPE = boost::unordered_map:

Reading symbols from /home/tim/Projects/gameproj/app/game...done.
(gdb) r
Starting program: /home/tim/Projects/gameproj/app/game 
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x0000000000499aca in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Font*> > > >::find_iterator (this=0x79bd80, 
    bucket=0x20, k=...) at /usr/local/include/boost/unordered/detail/table.hpp:55
55         node_ptr it = bucket->next_;
(gdb) bt
#0  0x0000000000499aca in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Font*> > > >::find_iterator (this=0x79bd80, 
    bucket=0x20, k=...) at /usr/local/include/boost/unordered/detail/table.hpp:55
#1  0x0000000000499872 in boost::unordered_detail::hash_table<boost::unordered_detail::map<std::string, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Font*> > > >::find (this=0x79bd80, k=...)
    at /usr/local/include/boost/unordered/detail/table.hpp:583
#2  0x00000000004994fb in boost::unordered_map<std::string, Font*, boost::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, Font*> > >::find (this=0x79bd80, k=...)
    at /usr/local/include/boost/unordered/unordered_map.hpp:423
#3  0x00000000004992ab in Resource<Font>::get (fileName=...) at /home/tim/Projects/gameproj/app/Resource.hpp:45
#4  0x0000000000498e23 in Menu::init (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Menu.cpp:57
#5  0x0000000000498ce1 in Menu::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Menu.cpp:23
#6  0x0000000000481275 in Main::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Main.cpp:25
#7  0x0000000000481135 in main (argc=1, argv=0x7fffffffe398) at /home/tim/Projects/gameproj/app/main.cpp:10
(gdb) 

Here is some output (stack trace) from gdb with MAP_TYPE = std::map:

Reading symbols from /home/tim/Projects/gameproj/app/game...done.
(gdb) r
Starting program: /home/tim/Projects/gameproj/app/game 
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff701ae4d in std::string::compare(std::string const&) const () from /usr/lib/libstdc++.so.6
(gdb) bt
#0  0x00007ffff701ae4d in std::string::compare(std::string const&) const () from /usr/lib/libstdc++.so.6
#1  0x0000000000480c2d in std::operator< <char, std::char_traits<char>, std::allocator<char> > (__lhs=..., __rhs=...)
    at /usr/include/c++/4.4/bits/basic_string.h:2320
#2  0x0000000000480a15 in std::less<std::string>::operator() (this=0x772d60, __x=..., __y=...)
    at /usr/include/c++/4.4/bits/stl_function.h:230
#3  0x0000000000480691 in std::_Rb_tree<std::string, std::pair<std::string const, Font*>, std::_Select1st<std::pair<std::string const, Font*> >, std::less<std::string>, std::allocator<std::pair<std::string const, Font*> > >::find (this=0x772d60, 
    __k=...) at /usr/include/c++/4.4/bits/stl_tree.h:1424
#4  0x0000000000480465 in std::map<std::string, Font*, std::less<std::string>, std::allocator<std::pair<std::string const, Font*> > >::find (this=0x772d60, __x=...) at /usr/include/c++/4.4/bits/stl_map.h:659
#5  0x000000000048027d in Resource<Font>::get (fileName=...) at /home/tim/Projects/gameproj/app/Resource.hpp:45
#6  0x000000000047ff97 in Menu::init (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Menu.cpp:57
#7  0x000000000047fe55 in Menu::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Menu.cpp:23
#8  0x000000000046a725 in Main::run (xResolution=1024, yResolution=768) at /home/tim/Projects/gameproj/app/Main.cpp:25
#9  0x000000000046a5e5 in main (argc=1, argv=0x7fffffffe398) at /home/tim/Projects/gameproj/app/main.cpp:10

I've also used Valgrind, there are no errors other than when the segmentation fault occurs.

I'm using CMake to generate Makefiles, gcc 4.4.3, and boost 1.43. I'm building on a 64 bit Ubuntu machine.

Any help on this would be greatly appreciated, I feel like I'm getting nowhere with it. In the meantime I'm going to try building on a different platform/machine to see if I get the same behavior.

A: 

Try changing

template <class T> std::string Resource<T>::c_dataDirectory;
template <class T> MAP_TYPE<std::string, T*> Resource<T>::c_resources;

to explicitly use the default constructors? Maybe this is required for templates--the C++ Annotations uses this style, although they don't comment on why.

If no one's mentioned it yet, there's one copy of the static data member per class.

Zachary Vance
A: 

Try using the Singleton Design Pattern. You can for example use Loki's SingletonHolder (http://loki-lib.sourceforge.net/index.php?n=Main.HomePage).

And if you have a proper Singleton object setup, you can skip the static members, since string and unordered_map anyway keeps all their members dynamically. Or make them static pointers, and be sure to delete them properly.

christoff