静态模板类的奇怪行为

时间:2010-07-09 23:08:52

标签: c++ templates boost segmentation-fault

我在诊断由于模板静态类产生的分段错误方面遇到了一些困难(请参阅原始帖子Help understanding segfault with std::map/boost::unordered_map)。

自发布以来,我在程序中发现了一些其他奇怪的行为。我从来没有遇到过这样的事情,我想我必须不知道模板如何工作的一些细微的细节,导致我在某处做错了。

我已经尝试过无意识地重构事情以使问题消失但仍然存在。

最令人困惑的症状是:在一个源文件(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中,程序遇到Menu.cpp中g_font = ...的分段错误(注意:Menu.cpp和Game.cpp中的所有内容都有自己的命名空间)。但是 NO 代码甚至已经在Game.cpp中执行了!

现在,如果我在Game.o之前链接Menu.o,问题就会消失(但其他地方还存在其他问题)。我在这里不理解什么?

我使用调试器逐步完成程序,看看发生了什么。当Game.cpp中注释掉g_font行时,Font的基类Resource中的boost容器(unordered_map)无法正确初始化。具体而言,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的完整列表(它现在<{1}} 潜在泄漏内存,我最初在容器中使用release()但切换到原始指针认为它会解决所有问题,doh)。

boost::shared_ptr

以下是gdb与#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__ 的一些输出(堆栈跟踪):

MAP_TYPE = boost::unordered_map

以下是gdb与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) 的一些输出(堆栈跟踪):

MAP_TYPE = std::map

我也使用过Valgrind,除了出现分段错误之外没有任何错误。

我正在使用CMake生成Makefile,gcc 4.4.3和boost 1.43。我正在使用64位Ubuntu机器。

对此的任何帮助都将非常感激,我觉得我无处可去。与此同时,我将尝试在不同的平台/机器上构建,看看我是否有同样的行为。

2 个答案:

答案 0 :(得分:0)

尝试更改

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

显式使用默认构造函数?也许这是模板所必需的 - C ++ Annotations使用这种风格,尽管他们没有评论为什么。

如果还没有人提到它,那么每个类的静态数据成员都有一个副本。

答案 1 :(得分:0)

尝试使用Singleton设计模式。例如,您可以使用Loki的SingletonHolder(http://loki-lib.sourceforge.net/index.php?n=Main.HomePage)。

如果你有一个正确的Singleton对象设置,你可以跳过静态成员,因为string和unordered_map无论如何动态地保留它们的所有成员。或者让它们成为静态指针,并确保正确删除它们。