J'ai eu quelques difficultés à diagnostiquer un défaut de segmentation résultant, ou du moins je pense, résultant d'une classe statique template'd (voir l'article original ici Help understanding segfault with std::map/boost::unordered_map).Comportement étrange avec la classe template'd statique
Depuis la publication que j'ai trouvé un autre comportement bizarre dans mon programme. Je n'ai jamais rencontré quoi que ce soit de pareil, et je pense que je ne dois pas être conscient de certains détails subtils sur le fonctionnement des modèles, ce qui me fait faire quelque chose de mal quelque part.
J'ai essayé de refactoriser des choses sans réfléchir pour essayer de faire disparaître le problème mais ça persiste.
Le symptôme le plus déconcertant est la suivante: dans un fichier source (Menu.cpp) Je les trois appels suivants:
Font::init();
// later
g_font = Font::get("Mono.ttf");
// later
Font::release();
Dans l'autre fichier source (Game.cpp) J'ai presque les trois mêmes lignes. Presque tout le code dans Menu.cpp est exécuté avant que n'importe quoi dans Game.cpp soit exécuté.
Maintenant tout va bien avec cela. Cependant, si je simplement commenter
g_font = Font::get("Arial.ttf");
dans Game.cpp le programme rencontre une erreur de segmentation à g_font = ...
en Menu.cpp (note: tout Menu.cpp et Game.cpp ont leur propre espace de noms). Mais le code NO a même été exécuté dans Game.cpp à ce stade!
Maintenant, si je lie Menu.o avant Game.o le problème disparaît (mais d'autres problèmes existent ailleurs). Qu'est-ce que je ne comprends pas ici?
J'ai utilisé un débogueur pour parcourir le programme pour voir ce qui se passe. Lorsque la ligne g_font
est mise en commentaire dans Game.cpp, un conteneur boost (unordered_map) dans la classe de base Font de la ressource n'est pas initialisé correctement. Plus précisément, buckets_
et size_
ne sont pas initialisés et entraînent un comportement erratique. J'ai essayé d'utiliser std :: map et des problèmes similaires existent.
Voici la liste complète des 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__
Voici la liste complète des Resource.hpp (il
potentiellement
fuites de mémoire en ce moment sur release()
, j'utilisais à l'origine boost::shared_ptr
dans le conteneur, mais commuté aux pointeurs bruts en pensant que cela réparerait tout, 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__
Voici une sortie (pile trace) de gdb avec 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)
Voici une sortie (trace de la pile) de gdb avec 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
J'ai également utilisé Valgrind, il n'y a pas d'autres erreurs que lorsque l'erreur de segmentation se produit. J'utilise CMake pour générer des Makefiles, gcc 4.4.3 et boost 1.43. Je construis sur une machine Ubuntu 64 bits.
Toute aide à ce sujet serait grandement appréciée, je me sens comme si je n'allais nulle part avec elle. En attendant, je vais essayer de construire sur une plate-forme/machine différente pour voir si j'ai le même comportement.
Votre programme est multithread? Vous n'avez aucun verrouillage. – Stephen
Actuellement, il n'y a qu'un seul thread. – Tim
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 – Anycorn