Archive | Visual Studio RSS for this section

DMP Photo Booth: Going All In With GLib Primitives

While working on DMP Photo Booth, I’ve been trying to use GLib library calls as often as possible as opposed to standard library calls. Why? Portability, of course. I’ve been doing my best to ensure my application is as portable as possible. While doing this, I find myself having to work with lots of glib types. After a few weeks of this, I find myself wondering if I shouldn’t be using these in my own code…

GLib Primitives

GLib defines g[type] versions of all standard C primitives, plus a few extras. The GLib reference manual describes these as falling into 4 different categories:

  • New types which are not part of standard C (but are defined in various C standard library header files) – gboolean, gsize, gssize, goffset, gintptr, guintptr.
  • Integer types which are guaranteed to be the same size across all platforms – gint8, guint8, gint16, guint16, gint32, guint32, gint64, guint64.
  • Types which are easier to use than their standard C counterparts – gpointer, gconstpointer, guchar, guint, gushort, gulong.
  • Types which correspond exactly to standard C types, but are included for completeness – gchar, gint, gshort, glong, gfloat, gdouble.

Let’s talk about these.

New Types

The new types represent boolean values, and size types. Both of these may have been new when GLib defined them, but they’ve since found their way into the C standard library since C99. That said, these are types that we often neglect to use. Who can honestly say they’ve never used a bare integer when a size_t would have been appropriate?

I feel that these are all good types to use, when needed. Maybe they no longer solve a real problem since C99, but GLib still uses them. I will use these for consistency’s sake, but the standard library types should suffice.

Integer Types

As we should all know, integers are not guaranteed to be the same size across different platforms. On platform A, an int may be 2 bytes, and on platform B, it may be 4 bytes. Meanwhile, these types provide this guarantee. Like the new types, C99 brings these types also, but in my opinion gint8 is easier to type than int_8.

Aside from ease, GLib uses its types, and for consistency I will too.

Types That Are “Easier To Use”

In my opinion, this one is debatable. The people over at Gnome may think that gpointer “looks better” and is “easier to use”, but I find it incredibly confusing. I’ve even mentioned this in past posts. Gpointer is a typedef of void *. In other words, where you may have done this before:

void * foo = malloc(sizeof(foo));

…you would now do this:

gpointer foo = g_malloc(sizeof(foo))

…and now you’re left with this gpointer foo;. Aside from the fact that it has the word “pointer” in the type name, this doesn’t look like a pointer. Even worse is when you see gpointer * bar;. At first glance, this looks like it should be a void * bar;, but it’s actually void * * bar;. I know I’ve spent more time than I care to admit trying to figure out why bar->the_bar was throwing compiler errors.

Maybe it’s just amateur hour in the DMP Undersea Laboratory, but I like to see the *. That way it’s right in my face that I’m dealing with a pointer. I can also count the number of *s to see how many array indices I’m dealing with. If I see void *** super_foo; I know I’m dealing with a 3 dimensional array just by counting the stars. I don’t have to think about it. On the other hand if I see gpointer ** super_foo; I have to know what gpointer is a typedef of; I can’t just trust my instincts.

For this reason, I will not be using gpointer or gconstpointer. The rest of the “easier to use” types can stay though.

Types That Correspond Exactly

There is not much to say about these. These types exist just for consistency’s sake. If you’re using GLib primitives, you should use these as well for consistency’s sake.

Seems Kind Of Pointless…

At first glance, yes it does. I must admit that while I was typeing this up, I wavered. “If all the GLib types are in the standard library in C99, why bother?” I thought. None of these types are objectivly better than the standard library versions, it’s just another name to remember. So why should we care?

The answer is because not all compilers/platforms support C99. The most nobale offender here is Microsoft Visual Studio. To quote Herb Sutter: “Our primary goal is to support “most of C99/C11 that is a subset of ISO C++98/C++11.”” This allegedly includes “C99 preprocessor and library extensions”, but this requires compiling with Visual Studio’s C++ compiler and accepting all the baggage that C++ brings. If you want to keep your code pure C, then you are stuck using C90 only.

So, long story short: if you want easy portability to Windows, consider using GLib primitives.

Moving Forward

It is time to get with the program. Development of DMP Photo Booth is still early-enough along that a refactor to use GLib primitives won’t be too difficult. I will not change the API for the modules because, while I will most likely use GLib with the reference modules, I don’t want to impose a requirement to use GLib. Int and char are exactly equivilent to gint and gchar, so this should not cause any issues.

However, moving forward, I’ll be using GLib primitives.

Pining for toString()

One of the things that I’ve had difficulty with coming from Java to C is dealing with strings. In Java, string handling is just so easy, they’re nice little objects that keep to themselves and don’t bother anybody. Everybody loves them, and wants to be them. Everybody has a method called toString() that lets them turn into one if you’ll just let them. Want to stick an EnterpriseBaseBeanFactoryFactory into a JLabel? EnterpriseBaseBeanFactoryFactory.toString() and you’re set. Wouldn’t it be nice if we had toString() in C?

About That

We sort of do. Like all things in C, it requires a bit of work, but we can replicate the effect. The function you are looking for is snprintf(), which is in stdio.h. snprintf() is a variadic function that works similarly to printf(). However, instead of printing to stdout, it places the string into a passed-in char *. snprintf() takes at least 3 arguments: a destination char *, the maximum number of bytes to output, and a format string. Following the format string, a variable number of extra arguments corresponding to the format string are passed in, similarly to printf().

Snprintf() does not do any pointer allocation or deallocation, so you should create your char * ahead of time. It can be a stack allocated char array too if you only need a temporary variable. The second argument should be less than or equal to the size of the char *. This size argument prevents snprintf() from writing off the end of your allocated memory.

For Example

Take the following code:

int value = 1000; char to_string[5]; int return_value = snprintf(to_string, 5, "%d", value); printf("Return value: %d, The String: %s", return_value, to_string);

This code creates a char array sized 5, and converts the number 1000. This code produces the following output:

Return value: 4, The String: 1000

…but 1000, is only 4 digits, right? Let’s see what happens if we reduce to_string[]’s size to 4, and the size argument to 4:

int value = 1000; char to_string[4]; int return_value = snprintf(to_string, 4, "%d", value); printf("Return value: %d, The String: %s", return_value, to_string);

…produces an output of:

Return value: 4, The String: 100

Snprintf() wrote “100” into the string and returned 4. What happened? When snprintf() tries to write a number of bytes greater than the limit, it returns the number of bytes that it would have written if the size was large enough. You can use this to test for failures and to determine how big your string needs to be. However, this does not explain why the string was truncated. Snprintf() returned 4, and our limit and array size were both 4, it should have worked, right?

The problem is that we are forgetting the terminating . Snprintf() appends to the end of strings it creates, and size needs to be left over for this null. However, the return value does not account for the terminating null.

What About Sprintf()?

In stdio.h, there is another function that appears to do the same thing: sprintf(). Sprintf() doesn’t take a size_t like snprintf() does. Surely this means that sprintf() does some sort of automatic checking right? No, it doesn’t. Sprintf() will happily write off the end of your passed-in char * and cause all sorts of problems. As we all know, this will result in undefined behavior. It might seem to work. It might segfault. It might even cause lions to emerge from your fridge and eat you. One thing I’m sure we can all agree on is: lions are terrifying. Do yourself a favor, and just use snprintf().

Portability Concerns

Like many things in C, word on the street is that snprintf() behaves differently across different platforms. This blog post was written using GCC and Linux. I can’t promise that snprintf() works exactly the same in Visual Studio, or on some random mainframe or whatever. As always, try it before you buy it.

Declare Your Default Constructor Private; Declare Your Class Non-Inheritable

There’s this book floating around: Effective C++. It contains tips on how not to be terrible at C++, and it’s not a terrible read too as far as programming books go. However, if you aren’t very careful, you can shoot yourself in the foot. Take item 6 for instance.

The gist of item 6 is: declare your default constructor, copy constructor, and operator= private if you don’t want them to be used. The compiler will declare these for you if you don’t declare them, so if you don’t want them to be used, you have to specifically disallow them. Having read this, I got into the habit of always making them private, and only allowing access to them as they become needed. This strategy worked fine until I tried to inherit from a base class with a private default constructor. Suddenly GCC decides to have a problem with this:

In file included from Seat.h:12:0, from Seat.cpp:8: Position.h:52:4: error: ‘clt::week3::Position::Position()’ is private Seat.cpp:22:28: error: within this context

With google proving unhelpful on this issue, I go through the process of creating a Visual Studio project and trying my code there. Visual Studio shows similar behavior. Has the lowly Chris Tetreault found a problem with the venerable Effective C++? As it turns out, no he didn’t. However, the book could have stood to elaborate on this subject. The book provides a base class that implements the uncopyable behavior, reproduced below:

class Uncopyable { protected: Uncopyable() {} // allow construction ~Uncopyable() {} // and destruction of // derived objects... private: Uncopyable(const Uncopyable&); // ...but prevent Uncopyable & operator=(const Uncopyable&); // copying };

The comments spell it out, that you have to make the default constructor protected, which is the same conclusion I came to in my troubleshooting. However, this is the only thing said on the subject. Either way, I guess I should have paid closer attention…

%d bloggers like this: