Can't load TTF addon in separate class file
cmasupra

Hello guys. I am attempting to make a game and learn Allegro/C++ as I go. I learned Java in high school and basic C++ last semester in college. I think I understand most concepts, but one I for sure don't understand is memory management because that is something I didn't have to deal with in Java, and that may be what is making my program not work.

I am currently working on the level editor for my game (I haven't started the game yet), and I am having trouble with the TTF addon not working. I can successfully use the addon in my main .cpp file, but it isn't working in a separate class that I have made. It keeps telling me that it failed to initialize the ttf addon.

Here is the code in the main file that creates an instance of the separate class "OpenDialogue", which creates a dialogue box (bitmap) for opening a previously-made level.

isShowingDialog = true;
currWorkPathCSTR = createDirectoryPath(currWorkPathAP, argv[0], "maps");
OpenDialogue oDialog (argv[0], "Open a Map", currWorkPathCSTR, ".dat");

Here is OpenDialogue.h:

#SelectExpand
1#ifndef OPENDIALOGUE_H 2#define OPENDIALOGUE_H 3#include <iostream> 4#include <stdio.h> 5#include <allegro5/allegro.h> 6#include <allegro5/allegro_image.h> 7#include <allegro5/allegro_font.h> 8#include <allegro5/allegro_ttf.h> 9 10#define UPPER_LIMIT_OF_POSSIBLE_MAPS 100 11 12class OpenDialogue 13{ 14 public: 15 int width, height; 16 17 OpenDialogue(const char argv[], const char* title, const char* path, const char* format); 18 void updateDialog(); 19 ALLEGRO_BITMAP *getBitmap(); 20 void checkClickLocationAndAct(ALLEGRO_MOUSE_STATE *mouse, int dialogueX, int dialogueY, int dialogueW, int dialogueH); 21 virtual ~OpenDialogue(); 22 protected: 23 private: 24 int returnValue; 25 ALLEGRO_BITMAP *mapListBMP; 26 ALLEGRO_BITMAP *dialogue; 27 ALLEGRO_FS_ENTRY *dir; 28 ALLEGRO_FS_ENTRY *mapsAvailable[UPPER_LIMIT_OF_POSSIBLE_MAPS]; 29 int numMapsLoaded; 30 ALLEGRO_PATH *fontPathAP; 31 const char *fontPathCSTR; 32 ALLEGRO_FONT *dialogueFont; 33 34 const char* switchToDirectory(ALLEGRO_PATH *currWorkPathAP, const char argv[], const char *addon); 35}; 36 37#endif // OPENDIALOGUE_H

And here is some of the code from OpenDialogue.cpp:

#SelectExpand
1#include "OpenDialogue.h" 2 3OpenDialogue::OpenDialogue(const char argv[], const char* title, const char* path, const char* format) 4{ 5 //ctor 6 //al_set_new_bitmap_flags(ALLEGRO_MIN_LINEAR); 7 //al_set_new_bitmap_flags(ALLEGRO_MAG_LINEAR); 8 al_init_font_addon(); 9 if(!al_init_ttf_addon()) 10 { 11 fprintf(stderr, "failed to initialize ttf addon in OpenDialogue.cpp!\n"); 12 returnValue = -1; 13 } 14 if(!al_init_image_addon()) 15 { 16 fprintf(stderr, "failed to initialize image addon in OpenDialogue.cpp!\n"); 17 returnValue = -1; 18 } 19 20 returnValue = 0; 21 numMapsLoaded = 0; 22 23 dialogue = al_create_bitmap(1000, 600); 24 if (dialogue == NULL) 25 { 26 fprintf(stderr, "failed to create dialogue BMP\n"); 27 returnValue = -1; 28 } 29 // read in file names from path and keep .dat files 30 //al_change_directory(path); 31 dir = al_create_fs_entry(path); 32 if (al_open_directory(dir)) 33 { 34 int i = 0; // current file 35 while( (mapsAvailable[i] = al_read_directory(dir)) && (mapsAvailable[i] != NULL) ) 36 { 37 //const char* mapNameCSTR = al_get_path_filename(al_create_path(al_get_fs_entry_name(mapsAvailable[i]))); 38 const char *fileExtCSTR = al_get_path_extension(al_create_path(al_get_fs_entry_name(mapsAvailable[i]))); 39 char *lowerFileExtCSTR; 40 strcpy(lowerFileExtCSTR, fileExtCSTR); 41 lowerFileExtCSTR = strlwr(lowerFileExtCSTR); 42 //printf("lowerFileExtCSTR: %s\n", lowerFileExtCSTR); 43 if (strcmp(lowerFileExtCSTR, format) == 0) 44 { 45 printf("%s", al_get_path_filename(al_create_path(al_get_fs_entry_name(mapsAvailable[i])))); 46 printf(" is a .dat\n"); 47 printf("done with file %i\n\n", i); 48 i++; 49 numMapsLoaded++; 50 } 51 } 52 } 53 else 54 { 55 fprintf(stderr, "failed to open open directory to make OpenDialogue instance!\n"); 56 returnValue = -1; 57 } 58 al_close_directory(dir); 59 60 // output data to mapListBMP 61 //printf("path: %s\n", path); 62 fontPathCSTR = switchToDirectory(fontPathAP, argv, "fonts"); 63 printf("fPathCSTR: %s\n", fontPathCSTR); 64 dialogueFont = al_load_font("FreeUniversal-Regular.ttf", 20, 0); 65 if (dialogueFont == NULL) 66 { 67 fprintf(stderr, "failed to create dialogueFont!\n"); 68 returnValue = -1; 69 } 70 mapListBMP = al_create_bitmap(600, 25*numMapsLoaded); // replace 550 with (20*numMapsLoaded) when I figure out how big each text thing is (also include width between each item) 71 al_set_target_bitmap(mapListBMP); 72 for (int i = 0; i < numMapsLoaded; i++) 73 { 74 al_draw_text(dialogueFont, al_map_rgb(255, 255, 255), 75 1, // destination starting x 76 i*25, // destination starting y 77 0, // flags 78 al_get_path_basename(al_create_path(al_get_fs_entry_name(mapsAvailable[i])))); // text to display 79 } 80 81 //updateDialog(); 82 //printf("created OpenDialogue instance\n"); 83}

Basically, at line 9 in OpenDialogue.cpp, it says that it failed to load the TTF addon (and it may fail at loading the main font addon, but I don't know since that one doesn't have a return). That causes the game to crash (as expected because dialogueFont is NULL) at line 74.

At line 64, it also tells me that it failed to create dialogueFont (as expected, because the TTF addon wasn't initialized).

Am I missing something obvious? I'm not sure if I even have to initialize the addons in every file or not.

Matthew Leverton

In main() the first things you should do are:

al_init();
al_init_font_addon();
al_init_ttf_addon();
// init whatever

You only need to do it once, and you must do it before you use any of their related functions. Don't put them in the constructors of your objects.

You need to be careful with constructors. Something like this won't work if the FooObject calls some other Allegro function because Allegro won't have been initialized yet:

FooObject Foo;

al_init();

bamccaig

Disclaimer: I am both intoxicated and too tired to bother reading through your OP.

"Class file" sounds like a Java influence. You must try to understand that C++ is very different from Java. C (the language that Allegro is written in) is even more different.

A file doesn't mean fuck all to C or C++. These compilers think in terms of "translation units". There are various stages of C or C++ building that you need to aware of:

  1. Preprocessing. The C preprocessor runs, processing directives (e.g., #include; or any line whose first non-whitespace character is a '#'), and effectively transforms the original source file (e.g., .c or .cpp) with them.

  2. Compilation. These translation units are effectively preprocessed source files. These are typically translated into object files. Effectively, the object file is a binary interpretation of the translation unit (i.e., preprocessed source file).

  3. Linking. The compiled translation units, as well as third-party libraries, are all linked together into some kind of executable output file.

(Think of each step above as a completely separate program)

The important distinction is that in a language like Java all of the available classes are available at once for interpretation and compilation (or at least, it seems practically so). In C or C++ each translation unit (preprocessed source file) only knows about itself (what it declares or defines) and whatever it #includes.

That said, much C or C++ code manipulates global data. In Allegro this is certainly true. Initializing Allegro or one of its addons (assuming you did so correctly) is global to the entire application. You only need to do so once. If it succeeds (see here) then it is considered initialized for the duration of the application (unless you happen to deinitialize it, but those APIs, if exposed at all, are rarely used in Allegro programs).

Glancing at your code I can say that it is wrong to initialize anything in Allegro within an "open dialog" method/function. That should typically not be handled by library routines, but instead by application code. If it is handled by a library routine it should be something more generic. That is to say, you don't need (nor want) to initialize Allegro nor its addons every time you open a dialog. You do it once for the duration of the application.

cmasupra

Here is the beginning of my main method in the main file. First come the variable declarations, then initializing Allegro stuff. I'm pretty sure it's in a good order.

#SelectExpand
1 ALLEGRO_DISPLAY *display = NULL; 2 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 3 ALLEGRO_TIMER *timer = NULL; 4 ALLEGRO_PATH *basePathAP = NULL; 5 ALLEGRO_PATH *currWorkPathAP = NULL; 6 const char* basePathCSTR = NULL; 7 const char *currWorkPathCSTR = NULL; 8 ALLEGRO_FS_ENTRY *dir = NULL; 9 ALLEGRO_FS_ENTRY *tilesAvailable[UPPER_LIMIT_OF_POSSIBLE_TILES]; 10 ALLEGRO_FS_ENTRY *itemsAvailable[UPPER_LIMIT_OF_POSSIBLE_ITEMS]; 11 int numTilesLoaded=0, numItemsLoaded=0; 12 ALLEGRO_MONITOR_INFO monitorInfo; 13 int monitorW, monitorH; 14 int amtToAddOnEachSide; // the amount of pixels to add/subtract on each side to make the game full screen 15 ALLEGRO_FONT *tileListFont = NULL; 16 bool redraw = true; 17 ALLEGRO_MOUSE_STATE mouse; 18 int mouseButton = 0; // used for determining which mouse button was released for MOUSE_BUTTON_UP event 19 int mouse1DownState = 0; // 0 = not holding down, 1 = holding down button 1 20 ALLEGRO_BITMAP *map = NULL; 21 int currTileInt = 0; 22 bool isShowingDialog = false; 23 24 25 if(!al_init()) 26 { 27 fprintf(stderr, "failed to initialize allegro!\n"); 28 al_destroy_fs_entry(dir); 29 al_destroy_fs_entry(*tilesAvailable); 30 al_destroy_fs_entry(*itemsAvailable); 31 al_destroy_bitmap(map); 32 al_destroy_event_queue(event_queue); 33 al_destroy_display(display); 34 return -1; 35 } 36 37 timer = al_create_timer(1.0 / 60.0); 38 if(!timer) 39 { 40 fprintf(stderr, "failed to create timer!"); 41 al_destroy_fs_entry(dir); 42 al_destroy_fs_entry(*tilesAvailable); 43 al_destroy_fs_entry(*itemsAvailable); 44 al_destroy_bitmap(map); 45 al_destroy_event_queue(event_queue); 46 al_destroy_display(display); 47 return -1; 48 } 49 50 //al_set_new_display_flags(ALLEGRO_FULLSCREEN); 51 al_set_new_display_refresh_rate(60); 52 al_get_monitor_info(0, &monitorInfo); 53 monitorW = monitorInfo.x2 - monitorInfo.x1; 54 monitorH = monitorInfo.y2 - monitorInfo.y1; 55 monitorW = 1366; 56 monitorH = 768; 57 amtToAddOnEachSide = 0.5 * (monitorH - (monitorW/16.0*9.0)); 58 display = al_create_display(monitorW, monitorH); 59 if(!display) 60 { 61 fprintf(stderr, "failed to create display!\n"); 62 al_destroy_fs_entry(dir); 63 al_destroy_fs_entry(*tilesAvailable); 64 al_destroy_fs_entry(*itemsAvailable); 65 al_destroy_bitmap(map); 66 al_destroy_event_queue(event_queue); 67 al_destroy_display(display); 68 return -1; 69 } 70 int lineWidthAt1920 = 6; 71 int lineWidth = roundTo0D(lineWidthAt1920 / 1920.0 * monitorW); 72 73 event_queue = al_create_event_queue(); 74 if(!event_queue) 75 { 76 fprintf(stderr, "failed to create event queue"); 77 al_destroy_fs_entry(dir); 78 al_destroy_fs_entry(*tilesAvailable); 79 al_destroy_fs_entry(*itemsAvailable); 80 al_destroy_bitmap(map); 81 al_destroy_event_queue(event_queue); 82 al_destroy_display(display); 83 } 84 85 if(!al_install_mouse()) 86 { 87 fprintf(stderr, "failed to initialize image addon!\n"); 88 al_destroy_fs_entry(dir); 89 al_destroy_fs_entry(*tilesAvailable); 90 al_destroy_fs_entry(*itemsAvailable); 91 al_destroy_bitmap(map); 92 al_destroy_event_queue(event_queue); 93 al_destroy_display(display); 94 return -1; 95 } 96 97 98 if(!al_init_image_addon()) 99 { 100 fprintf(stderr, "failed to initialize image addon!\n"); 101 al_destroy_fs_entry(dir); 102 al_destroy_fs_entry(*tilesAvailable); 103 al_destroy_fs_entry(*itemsAvailable); 104 al_destroy_bitmap(map); 105 al_destroy_event_queue(event_queue); 106 al_destroy_display(display); 107 return -1; 108 } 109 110 al_init_font_addon(); 111 al_init_ttf_addon();

I just tried removing the Allegro initializations from OpenDialogue.cpp, and it's still crashing, but it didn't crash at the same point this time. I can't figure out where it's crashing.

Regardless, I somehow have it working. It makes no sense to me. I hope someone can explain it. Here is what my new oDialog declaration looks like (same as before, but with printf statements).

isShowingDialog = true;
currWorkPathCSTR = createDirectoryPath(currWorkPathAP, argv[0], "maps");
printf("creating oDialog instance"); // ?????? commenting this printf out makes the game crash in the OpenDialogue class in the same spot as before ????
OpenDialogue oDialog (argv[0], "Open a Map", currWorkPathCSTR, ".dat");
printf("created oDialog instance");

The only thing changed from the OpenDialogue.cpp class shown in my previous post is I added some prinf statements in there and commented out the Allegro initializations, but that's not the confusing part. The confusing part is in the code right above this paragraph. Commenting out that printf statement makes the game crash. That makes no sense to me. Maybe there are memory management problems and printf is somehow accessing memory and coincidentally fixing it? That seems pretty farfetched to me.

Thread #607658. Printed from Allegro.cc