Strange TTF_CloseFont and freetype crash

Hello everyone,

I am trying to figure out a problem that I am having with deleting my ttf_Font wrapper class. Everything is working fine expect when I place my wrapper class inside another class.
My ttf_Font wrapper class looks like this:

    //font.h
	class Font {
	public:
		TTF_Font* font = NULL;

		Font();
		Font(std::string file, int size);
		~Font();
	};

	//font.cpp
	Font::Font() {
	}

	Font::Font(std::string file, int size) {
		font = TTF_OpenFont(file.c_str(), size);
		if (font == NULL)
			std::cout << "Could not open font file " << file << "\n" << TTF_GetError() << "\n\n";
	}

	Font::~Font() {
		std::cout << "del" << std::endl;
		TTF_CloseFont(font);
		font = NULL;
	}

Everything works when the rest of my code code looks like this:
(I have removed error checking code on purpose.)

void run() {
	Font font("res/fonts/pixelBubble.ttf", 50);
}

int main(int argc, char *argv[]) {	
	SDL_Init(SDL_INIT_EVERYTHING);
	TTF_Init();

	run();
	
	TTF_Quit();
	SDL_Quit();

	return 0;
}

But if I try to execute this code…:

struct Test {
	Font font;

	Test() {
		font = Font("res/fonts/pixelBubble.ttf", 50);
	}
};

void run() {
	Test test;
}

int main(int argc, char *argv[]) {
	SDL_Init(SDL_INIT_EVERYTHING);
	TTF_Init();

	run();
	
	TTF_Quit();
	SDL_Quit();

	return 0;
}

…the program crashes on TTF_CloseFont().
Visual Studio gives me this error:

Exception thrown at 0x6CD08DDB (libfreetype-6.dll) in SDL_Project.exe: 0xC0000005: Access violation reading location 0x00000038.

And when I break the program, it opens a “No Symbols Loaded” file:

Symbol file not loaded

No symbol file for libfreetype-6.dll

Module Information
Version:
Original Location: C:\CppProjects\SDL_Project\SDL_Project\libfreetype-6.dll

I have no idea why I get this error. So I hope someone can explain the problem, and can provide a solution. Thanks in advance.

My guess is that the temporary object (an rvalue) in your initialization is destructed just after assignment, then when your font variable is destructed, it’s a double-free of the memory.

Try changing this:

	Test() {
		font = Font("res/fonts/pixelBubble.ttf", 50);
	}

To:

Test()
: font("res/fonts/pixelBubble.ttf", 50)
{
    
}

To say it in a more lengthy way… your call to Font() creates a font. Then the assignment (=) shallow-copies all of the font data into your variable font. Then the Font() part gets destructed, which invalidates the data you just stored in font. When font is finally either used or destructed, naughty things happen and the computer is not happy about it.

The change I made here is to use the constructor for font in an initializer list instead of letting it get default-constructed and then using assignment. A few other ways to fix this: Write the assignment with move semantics so you can steal the resources from the temporary object and leave the temp in a state where destructing it is safe. Write an assignment operator that actually copies the data (might not be possible with SDL_ttf’s API). Write a wrapper for the TTF_Font that implements a refcount so its ownership can be shared.

I can confirm that (at least with gcc). You should see two “del” messages. (I also added a loaded message here)

loaded 2148058184
del 2148058184
del 2148058184

Thread 1 "font" received signal SIGSEGV, Segmentation fault.
0xb7a62119 in FT_Done_Face () from /usr/lib/libfreetype.so.6
(gdb) bt
#0  0xb7a62119 in FT_Done_Face () from /usr/lib/libfreetype.so.6
#1  0xb7efb3a4 in TTF_CloseFont () from /usr/lib/libSDL_ttf-2.0.so.0
#2  0x80001024 in Font::~Font (this=0xbffffa18, __in_chrg=<optimized out>) at font.cpp:18
#3  0x80000e9f in Test::~Test (this=0xbffffa18, __in_chrg=<optimized out>) at main.cpp:5
#4  0x80000d03 in run () at main.cpp:14
#5  0x80000d4e in main (argc=1, argv=0xbffffae4) at main.cpp:21

Thanks, that works! I think I understand the problem, now I can try to apply the solution to my FontManager class :slight_smile:

Cool. Also, I want to point out that std::shared_ptr does allow for specifying a custom delete function, so one could use that instead of implementing one’s own refcount.