2010-07-09 5 views
1

У меня возникли некоторые трудности с диагностикой ошибки сегментации в результате или, по крайней мере, я думаю, что это связано с статическим классом template'd (см. Исходный пост здесь Help understanding segfault with std::map/boost::unordered_map).Странное поведение со статическим классом template'd

После публикации, что я нашел в моей программе какое-то другое странное поведение. Я никогда не сталкивался с чем-либо подобным, и я думаю, что я должен не знать некоторых тонких деталей о том, как работают шаблоны, в результате чего я что-то делаю неправильно.

Я пытался бездумно реорганизовать вещи, чтобы попытаться устранить проблему, но она по-прежнему сохраняется.

Наиболее озадачивает симптом заключается в следующем: в одном исходном файле (Menu.cpp) У меня есть следующие три вызовов:

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

В другом исходном файле (Game.cpp) У меня почти такой же три линий. Почти весь код в Menu.cpp выполняется до того, как все будет выполнено в Game.cpp.

Теперь все в порядке с этим. Однако, если я просто закомментируйте

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

в Game.cpp программа встречает ошибку сегментации на g_font = ... в Menu.cpp (примечание: все в Menu.cpp и Game.cpp имеют свое собственное пространство имен). Но NO код даже был выполнен в Game.cpp на этом этапе!

Теперь, если я свяжусь с Menu.o до Game.o, проблема исчезнет (но другая проблема существует в другом месте). Что я здесь не понимаю?

Я использовал отладчик, чтобы перейти через программу, чтобы узнать, что происходит. Когда строка g_font прокомментирована в Game.cpp, контейнер повышения (unordered_map) в базовом классе Font Resource каким-то образом не инициализируется правильно. В частности, buckets_ и size_ неинициализированы и приводят к неустойчивому поведению. Я пробовал использовать std :: map и аналогичные проблемы.

Вот полный список для 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__ 

Вот полный список для Resource.hpp (это потенциально утечки памяти прямо сейчас на release(), я был первоначально используя boost::shared_ptr в контейнере, но переключились на сырые указатели, думая, что все это исправит, да).

#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__ 

Вот некоторые вывода (трассировка стека) из GDB с 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) 

Вот некоторые вывода (трассировка стека) из GDB с 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 

Я также используется Valgrind, ошибок нет, кроме случаев, когда возникает ошибка сегментации.

Я использую CMake для создания Makefile, gcc 4.4.3 и boost 1.43. Я строю на 64-битной машине Ubuntu.

Любая помощь по этому поводу будет очень благодарна, я чувствую, что с ней ничего не получается. Тем временем я собираюсь попытаться построить другую платформу/машину, чтобы убедиться, что я получаю такое же поведение.

+0

Является ли ваша программа многопоточной? У вас нет блокировки. – Stephen

+1

В настоящее время существует только одна тема. – Tim

+1

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 – Anycorn

ответ

0

Try изменения

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

явно конструкторами по умолчанию? Возможно, это требуется для шаблонов - C++ Annotations использует этот стиль, хотя они не комментируют почему.

Если об этом никто не упоминал, имеется одна копия статического элемента данных для каждого класса.

0

Попробуйте использовать шаблон дизайна Singleton. Например, вы можете использовать SingletonHolder от Loki (http://loki-lib.sourceforge.net/index.php?n=Main.HomePage).

И если у вас есть подходящая установка объекта Singleton, вы можете пропустить статические элементы, так как строка и unordered_map в любом случае сохраняют все их члены динамически. Или сделайте их статическими указателями и обязательно удалите их правильно.

Смежные вопросы