Well, it’s been a long time coming, but this weekend 45 days of work has culminated in my first milestone: DMP Photo Booth has received its first commit. If you go to this page, you’ll find the newly created Github repository for it. Feel free to go judge all the choices I made, I’ll wait…
…done? Good, now I’d like to use this post to talk about it.
The Story So Far
So far, most of the work is done in the core. There are module stubs for each module, and they are loadable and “function”, but they don’t do much of anything. However, the UI for the core is, for the most part, done. If you build the core (Either open the projects in NetBeans or cd into its directory and enter
make build), and run it (as of this writing, it must be run from the project root directory, since the supporting files are located here and referenced by relative paths) it will launch. At this point, you can launch the about window, the config window, or exit the program. From the config window you can load your modules (built stub modules are included for your convenience), and the status indicators on the main window will detect it.
All this is fine and good, but the real story here is the plumbing that has been laid.
Not much works at the moment, but the groundwork has been laid for things to come. Going down the list:
In this header, you’ll find all the magic integer constant return values used throughout DMP Photo Booth and friends. I touched on this topic briefly in a previous post, but the cliff’s notes version is that I feel that using a bare integer instead of an enum reduces the burden on module developers, and that the benefits of an enum aren’t great enough to justify this burden.
As this header has no accompanying .c file, it can be
#included by module developers so they can make use of it if they like. I do this in my modules.
One of the first things I worked on was module handling. This header holds all the functions required for the loading and unloading of modules, and the calling of module functions. At first I was implementing all of this with POSIX calls, but it occurred to me that I was defeating the purpose of my modular architecture by using non-portable calls. For this reason, I switched to GModule for module handling. I wrote a post about this if you want to hear more.
This header contains functions that will be added to modules as callbacks. Currently, this is the function that will be called when the user presses “the button”, and the function that will be called to push a message to the console queue.
My general approach to function design is that functions should not return void. All functions that would, instead return gint. This way, I have a mechanism to detect error conditions. In a language without Exception Handling, this is a nice thing to have. This header contains functions to extend this. Since sometimes you can’t return gint, I’ve created a facility to log error codes, similar to how the POSIX errno function works.
This header contains a thread safe queue to store messages to print to the console. Currently, this is implemented with a GQueue and mutex locking, but I made that choice before I knew about GAsyncQueue. I may refactor this at some point to use GAsyncQueue, but for now it works fine, and I see no immediate issue with my implementation.
The idea behind the console queue is that there should be a generic way to store messages. Currently the console is a GtkTextBuffer, but if I build around this, it limits future functionality. Suppose I wanted to support a command line only option? It’d be silly to require GTK for that, wouldn’t it?
That said, the default implementation uses a function placed in the GtkMainLoop to pop all messages out of the queue and place them into the GtkTextBuffer.
This header file is in charge of keeping those fancy module status indicators up to date. There are two main moving parts here: a thread that polls the module status, and a function run by the GtkMainLoop.
The Thread polls the modules every 3 seconds and pushes any status changes to a GAsyncQueue. Meanwhile the GtkMainLoop checks the GAsyncQueue for updates, and sets the indicator status accordingly. Why doesn’t the GtkMainLoop just check the module status you ask? I wanted to make sure the GtkMainLoop does as little work as possible. Currently the calls to check module status are pretty trivial, but there is no guarantee that they will stay that way. Since nobody likes an unresponsive interface, I kept them separate.
This rather hefty header contains two things: a function to build and launch the UI, and all the callbacks called by said UI. All the callbacks are named according to their glade widget name to maintain some semblance of readability.
The Next Step
Obviously, looking through the User Interface source, there are signals to be hooked up. But aside from that, the core is coming along. The one big portion of the core remaining to be done is the photo booth strip logic. The camera module will take X pictures and download them. At this point it is up to the core to assemble them into on image file to be printed.
For now, I will continue to hook up signals and refine the existing portions of the core. While I do this, I will research image handling libraries. I’d like to keep things within the GLib family, but failing this, I will try to keep it limited to libraries shipped with Ubuntu (The default platform). Windows compatibility will come, but version 1.0 will work on Ubuntu.
Also looming on the horizon are the modules. I have a general idea of how I will deal with these, but nothing is set in stone. The first module that I will likely handle is the trigger module. I plan to implement the trigger module using Arduino. This is a topic that interest me, but I’ve never had a good project idea. Whenever I get tired of dealing with GTK, it should be a nice change of pace.
As anybody who has ever developed in a modern object-oriented or scripting language and then learned C can tell you, working with strings in C sucks. It sucks really hard. Let me list some of the ways that C strings are terrible:
- Strings are bare char arrays in C; they require manual memory management
- As pointers, C strings are allocated on the heap. Unless they are string literals, in which case they are magical immutable things. Don’t you dare attempt to free() a string literal
- There is no way to test if a char array is a heap-allocated pointer or a string literal
- C strings have to be null terminated, or nothing works
- There is no reliable way to tell the length of a C string; strlen() will give incorrect results if the string isn’t null terminated.
- Copying a C string requires you to have allocated a char array greater than or equal to the size of the string to be copied. However, strlen() returns the size required, -1
- String concatenation requires the management of 2 pointers
- Most C string functions have “n” versions, that you should use instead of the base versions if you don’t want your program to be hacked
I’m sure there are others, but it’s late. The point here is that dealing with C strings is kind of a drag. Unfortunately for us, this comes with the territory; losing the baggage of object orientation also requires us to lose its conveniences. Or so I thought…
GString To The Rescue
While looking through the reference manual for GLib, I noticed a section: Strings. You read that right, GStrings are a thing! GLib provides a string class that does all the usual sorts of things you’d expect of a proper string object. Let’s talk about a few of them.
This is your string constructor. The documentation says that it “Creates a new GString, initialized with the given string.” You’ll notice that it doesn’t say who is responsible for the original char * when the string instantiation is complete. In search of the truth, I turned to Google.
Shockingly, Google proves unhelpful. After poking around, I decide to just look in the source code. Under the covers,
g_string_new calls out to
g_string_append_len, which in turn calls out to
g_string_insert_len. G_string_insert_len does some pointer jimmying, then finally calls memcpy to copy the contents of the char array into the string. The verdict: the GString does not take ownership of your char array; make sure you free it (if it’s not a string literal, of course) when you’re done with it.
This is your string destructor. This function takes two arguments: the string to free, and a gboolean. The gboolean is important; if you set it TRUE, it will free the underlying char array inside the GString. If you set it FALSE, then you are on the hook for its memory management. This is useful you want to return the char array from a function and you don’t need the GString object any more.
g_string_append and friends
GString provides a suite of string concatenation functions. You can append at the front or the end of a GString. The best part is that you don’t even have to worry about resizing the array; GString does all that for you!
g_string_printf and friends
GString provides several printf functions that function pretty much how you’d expect: they facilitate writing formatted strings into a GString using the semantics of printf. This allows you to convert non-string types to strings, and format your data. For us Java types, this is your FooType.toString() method replacement.
GString has three public fields: str, len, and allocated_len. Str is a pointer to the underlying C string. If you need to access the actual data of a GString, just call
the_string->str. Len is the length of the string. This would be equivilent to a call to
strlen(), but it is not dependent on a terminating null character. In fact, a GString can have null characters throughout without issue! Finally, allocated_len is the actual allocated size of str.
Consequences Were Never The Same
GString solves many of the issues of C Strings:
- GStrings encapsulate your C Strings; no longer are you subject to the tyranny of malloc
- If a function returns a GString, you need not worry if it is a heap allocated pointer or a string literal
- GStrings don’t need to be null terminated
- Testing the length of a GString is a simple matter of checking its
- Copying a GString is a simple call to
- String concatenation is a simple method call requiring no math
In a world with GStrings, string management in C became just a bit more tolerable. Object-oriented refugees worldwide give their heartfelt thanks to the brave men and women who gave us the GString.