【问题标题】:Strange behavior with static template'd class静态模板类的奇怪行为
【发布时间】:2011-03-14 02:58:46
【问题描述】:

我在诊断由模板静态类引起的分段错误时遇到了一些困难,或者至少我认为是由模板静态类引起的(请参阅此处的原始帖子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,问题就会消失(但其他地方存在其他问题)。我在这里不明白什么?

我使用调试器单步执行程序以查看发生了什么。当 g_font 在 Game.cpp 中被注释掉时,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 的完整列表(它可能现在在release() 上泄漏内存,我最初在容器中使用boost::shared_ptr,但切换到原始指针认为它会修复一切,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__

这是来自 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 机器上构建。

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

【问题讨论】:

  • 你的程序是多线程的吗?你没有任何锁定。
  • 目前只有一个线程。
  • @aaa :我也在想同样的事情,但这发生在 main() 之后,并且没有静态顺序依赖,只是在调用 ::init() 时构造了映射。我认为调用 init 需要初始化 resource.cpp。
  • @aaa:感谢您的链接,但正如斯蒂芬所说,没有静态订单依赖项。我使用 C++faq 中描述的模式保护了 Resource.hpp 中的静态成员变量,但是,正如预期的那样,它没有改变任何东西。

标签: c++ templates boost segmentation-fault


【解决方案1】:

尝试改变

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2021-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多