Sign in to follow this  
andrew_480

Help me jebus! - shared_ptr

Recommended Posts

andrew_480    163
I can't fugure this out. I can't get my SDL_Font class into a map. It gives me this error: Fatal signal: Segmentation Fault (SDL Parachute Deployed) I have initialised everything properly. I know for a fact that SDL_Init() and TTF_Init() were called successfully. Here's the class: SDLWrapper_Font.h:
#ifndef _SDL_WRAPPER_FONT_H_
#define _SDL_WRAPPER_FONT_H_

#include <SDL/SDL_ttf.h>
#include <map>
#include <string>

namespace AWT
{
  
  class SDL_Font
  {
    private:
      TTF_Font* m_font;
    public:
      // Constructor.
      SDL_Font(void) : m_font(0) { }
      
      // Destructor.
      ~SDL_Font(void) { TTF_CloseFont(m_font); }
      
      // Load a font.
      bool load(::std::string fileName, int size);
      
      // Render some text solid.
      SDL_Surface* renderTextSolid(::std::string text, Uint8 r, Uint8 g, 
      Uint8 b, int style);
      
      // Render some blended text.
      SDL_Surface* renderTextBlended(::std::string text, Uint8 r, Uint8 g, 
      Uint8 b, int style);
      
      // Render some shaded text.
      SDL_Surface* renderTextShaded(::std::string text, Uint8 r, Uint8 g, 
      Uint8 b, Uint8 bgR, Uint8 bgG, Uint8 bgB, int style);
      
      // Get the size that the text would be rendered to.
      bool sizeText(::std::string text, int& w, int& h);
  };
  
  class SDL_FontManager
  {
    private:
      ::std::map < ::std::string, SDL_Font > m_fonts;
    public:
      // Constuctor.
      SDL_FontManager(void);
      
      // Destructor.
      ~SDL_FontManager(void) { TTF_Quit(); }
      
      // Load a font from file.
      SDL_Font* load(::std::string fileName, ::std::string alias, int size);
  };
  
} // AWT

#endif



SDLWrapper_Font.cpp:

#include "SDLWrapper_Font.h"
#include "SDLWrapper_Log.h"

namespace AWT
{
  // Load a font.
  bool SDL_Font::load(::std::string fileName, int size)
  {
    TTF_Font* temp;
    
    temp = TTF_OpenFont(fileName.c_str(), size);
    
    if (temp == 0)
    {
      report("Couldn't load font " + fileName + ".");
      return false;
    }
    
    if (m_font != 0)
    {
      TTF_CloseFont(m_font);
    }
    
    m_font = temp;
    
    return true;
  }
  
  // Render some text solid.
  SDL_Surface* SDL_Font::renderTextSolid(::std::string text, Uint8 r, Uint8 g, 
  Uint8 b, int style) 
  {
    SDL_Color fg;
    fg.r = r;
    fg.g = g;
    fg.b = b;
    
    return TTF_RenderText_Solid(m_font, text.c_str(), fg);
  }
  
  // Render some blended text.
  SDL_Surface* SDL_Font::renderTextBlended(::std::string text, Uint8 r, Uint8 g,
  Uint8 b, int style) 
  {
    SDL_Color fg;
    fg.r = r;
    fg.g = g;
    fg.b = b;
    
    return TTF_RenderText_Blended(m_font, text.c_str(), fg);
  }
  
  // Render some shaded text.
  SDL_Surface* SDL_Font::renderTextShaded(::std::string text, Uint8 r, Uint8 g, 
  Uint8 b, Uint8 bgR, Uint8 bgG, Uint8 bgB, int style)
  {
    SDL_Color fg, bg;
    fg.r = r;
    fg.g = g;
    fg.b = b;
    bg.r = bgR;
    bg.g = bgG;
    bg.b = bgB;
    
    return TTF_RenderText_Shaded(m_font, text.c_str(), fg, bg);
  }
  
  // Get the size that the text would be rendered to.
  bool SDL_Font::sizeText(::std::string text, int& w, int& h)
  {
    // Get the size.
    if (TTF_SizeText(m_font, text.c_str(), &w, &h) < 0)
    {
      w = 0;
      h = 0;
      report("Couldn't size text.");
      return false;
    }
    return true;
  }
  
  // Constructor.
  SDL_FontManager::SDL_FontManager(void) : m_fonts()
  {
    // Check that TTF isn't already initialized.
    if (TTF_WasInit())
    {
      report("SDL_ttf has already been initialized.");
      return;
    }
    
    // Initialize TTF. If an error occurs throw an exception.
    if (TTF_Init() < 0)
    {
      report("Couldn't initialize SDL_ttf.");
    }
  }
  
  // Load a font from file.
  SDL_Font* SDL_FontManager::load(::std::string fileName, ::std::string alias, int size)
  {
    ::std::map < ::std::string, SDL_Font >::iterator itr = m_fonts.find(alias);
    
    if (itr != m_fonts.end())
    {
      report("Couldn't load font " + fileName + " as " + alias + 
      ". Alias already exists.");
      return &(itr->second);
    }
    
    // ----------> Here comes the error <-----------
    if (!m_fonts[alias].load(fileName, size))
    {
      m_fonts.erase(m_fonts.find(alias));
      return 0;
    }
    
    return &(m_fonts[alias]);
  }
  
} // AWT




Main.cpp:
#include <string.h>
#include <SDL/SDL.h>
#include <windows.h>

#include "../INCLUDE/SDLWrapper_Application.h"

int main (int argc, char *argv[])
{
  AWT::SDL_Application app(SDL_INIT_EVERYTHING); // Inits SDL and TTF.
  app.window.setVideoMode();
  
  app.fonts.load("C:/Windows/Fonts/ARIAL.TTF", "arial", 12);
  
  int done = 0;
  while (!done)
  {
    SDL_Event event;

    while (SDL_PollEvent (&event))
    {
      done = app.handleEvent(&event);
    }
    
    app.wait(10);
    app.window.flip();
  }

  return 0;
}


[Edited by - andrew_480 on April 18, 2006 12:17:59 AM]

Share this post


Link to post
Share on other sites
DeadXorAlive    535
I'm a bit tired so I didn't look over all the code, but one thing that's a problem is that you have a type in a map that doesn't copy properly. The standard containers use copy constructors and assignment (iirc) operators internally to do their magic. Since SDL_Font has a pointer to a TTF_Font, this might be the problem (it copies only the adressess of the fonts, not the fonts themselves). You could write a copy constructor and assignment operator, but it's better to store pointers to SDL_Font (less overhead of all that copying) or even better: boost::shared_ptr.
Cheers, hope this makes sense.

Share this post


Link to post
Share on other sites
andrew_480    163
How do I set a shared_ptr to NULL. I tried ptr = 0, but it says it can't convert between integer and shared_ptr.

[Edited by - andrew_480 on April 18, 2006 2:16:21 AM]

Share this post


Link to post
Share on other sites
DeadXorAlive    535
Quote:
Original post by andrew_480
How do I set a shared_ptr to NULL. I tried ptr = 0, but it says it can't convert between integer and shared_ptr.



shared_ptr<stuff> Handle(new stuff());
if (Handle) // Checks if pointer is valid
/*do stuff*/
Handle.reset();


You can get the raw pointer of a shared_ptr with get()

In practice you rarely need to do this. If you put all the shared_ptr's in a logical scope, memory management is automated so you don't have to set anything to NULL.

Share this post


Link to post
Share on other sites
andrew_480    163
Okay, here's what I have ended up with:

template <typename T>
class FileLoader
{
private:
::std::map < ::std::string, ::boost::shared_ptr<T> > m_data; // All of the files and their file names.
public:
// Constructor.
FileLoader(void) : m_data() { }

// Destructor.
~FileLoader(void) { }

// Get data.
::boost::shared_ptr<T> get(::std::string fileName)
{
typename ::std::map < ::std::string, ::boost::shared_ptr<T> >::iterator itr = m_data.find(fileName);

if (itr == m_data.end())
{
::boost::shared_ptr<T> result;
return result;
}

return itr->second;
}

// Just load the file.
::boost::shared_ptr<T> load(::std::string fileName)
{
typename ::std::map < ::std::string, ::boost::shared_ptr<T> >::iterator itr =
m_data.find(fileName);

if (itr != m_data.end())
{
::boost::shared_ptr<T> result;
return result;
}

m_data[fileName] = ::boost::shared_ptr<T>(new T());
if (m_data[fileName]->load(fileName) == false)
{
m_data.erase(m_data.find(fileName));
::boost::shared_ptr<T> result;
return result;
}
return m_data[fileName];
}

// Just load the file - For fonts.
::boost::shared_ptr<T> load(::std::string fileName, int size)
{
typename ::std::map < ::std::string, ::boost::shared_ptr<T> >::iterator itr = m_data.find(fileName);

if (itr != m_data.end())
{
::boost::shared_ptr<T> result;
return result;
}

m_data[fileName] = ::boost::shared_ptr<T>(new T());
if (m_data[fileName]->load(fileName, size) == false)
{
m_data.erase(m_data.find(fileName));
::boost::shared_ptr<T> result;
return result;
}
return m_data[fileName];
}

// Delete data.
bool erase(::std::string fileName)
{
typename ::std::map < ::std::string, ::boost::shared_ptr<T> >::iterator itr = m_data.find(fileName);

if (itr == m_data.end())
{
return false;
}

m_data.erase(itr);
return true;
}

// Delete all data.
void clear(void)
{
m_data.clear();
}

// Returns true if the data exists.
bool isLoaded(::std::string fileName)
{
return (m_data.find(fileName) != m_data.end());
}

Uint16 size(void)
{
return m_data.size();
}

::boost::shared_ptr<T> add(::std::string name)
{
if (m_data.find(name) != m_data.end())
{
::boost::shared_ptr<T> result;
return result;
}
m_data[name] = ::boost::shared_ptr<T>(new T());
return m_data[name];
}

void clearUnused(void)
{
typename ::std::map < ::std::string, ::boost::shared_ptr<T> >::iterator itr;

for (itr = m_data.begin();itr != m_data.end();itr++)
{
if (itr->second.use_count() < 2)
{
m_data.erase(itr);
}
}
}
};


I'm not sure if using
::boost::shared_ptr<T> result; return result;
was a good way of doing it, but it works.

Share this post


Link to post
Share on other sites
DeadXorAlive    535
Cool, looks good to me. Just some notes since I've read the code already:

- passing std::strings as const std::string& may give some faster code and imo states your intent to the caller in a clearer way.

- i think you can say return boost::shared_ptr<T>();. (probably compiler will optimize this anyway, but it's somewhat easier to read). Other than throwing exceptions I think this is the right way, because your fileloader cannot and must not determine what to do on file error, imo that's the job of the caller, he must do that anyway. In a similar piece of code I write the error message and filename to an html file but otherwise do the same, maybe exceptions are better I don't know really.

- if you use this for textures for example, one thing you could do is when a new texture is loading, check for all shared_ptr's if they are unique (there's a method for that), and if so erase them. That way you won't have to deal with manually managing these resources anymore. So you have a mini-garbage collector. [smile] Actually what you have now does more then loading files, you might want to rename it.
Cheers.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this