开发者

Why use GLib functions?

开发者_Go百科

While programming in C and GTK+, why is it "better" to use g_strdup_printf, g_free, g_strcmp0 etc... and fellow GLib functions?


In general, GLib's purpose is a utility and portability library. Those in itself are reasons to consider using it.

The specific functions you mention all offer something extra on top of their C standard library variants:

  • g_strdup_printf is like sprintf, but actually allocates the buffer for you and saves you the guesswork of how large the buffer should be. (The return value should be g_free'd.)
  • g_free is like free, but checks for a NULL-pointer.
  • g_strcmp0 is like strcmp, but treats a NULL-pointer like an empty string, and thus sorts it in front.


For consistent behavior in multiple operating systems. It's a portability thing.

In some other unix environments other than Linux, or if your program is compiled on windows, some of those functions may not exist or behave differently on the target operating system.

Using the glib versions ensure consistent behavior.


The GLib provides portability and basic stuff you'd expect nowadays from any programming language, like collection types (linked lists, arrays, hash tables, etc.). Here are some of the benefits GLib can give you.

Portability


The point of the GLib is to be portable not to the C standard, but to the implementations of the standard. The GLib takes care of the known quirks that might seem useless at first sight until you need to port your code to a platform that has those nasty bugs.

Let's take the example of g_free, as many criticize it. There are platforms where free(NULL) will fail, even if C99 says it should work. That check exists since at least 1998 (I tracked it in git history). Some may say it's not needed anymore, but even in 2017 I worked at a company that checks for NULL before calling free because otherwise it would crash on their embedded platform. It also serves as a wrapper for intrumentation of your code when you want to do some serious memory debugging.

Readability


It helps improving the readability of your code by providing some wrapper functions that not only improve portabitlity, but also help you avoid many language pitfalls. How many of you test malloc to see if it returns NULL? How many of you have a way to recover if it returns NULL, since you're basicly out of memory?

g_malloc will abort the application if it can't allocate what you want, and in many applications, this is just the behavior you want. For very big allocations that may fail, you have g_try_malloc. That is the same as malloc but still gives you the benefit of being a wrapper that may be used for instrumentation.

Being able to write:

char *buffer = g_malloc(30);
/* Do something with it ... */
g_free (buffer);

...frees the mind and lets the developer focus on the task she's trying to achieve. It also avoids having your program crash much later because it's trying to write using NULL pointer and you have to track down the allocation.

The standard C library is full of traps, and not having to micro manage every single line of code you write is a relief. Just read the BUGS section of the manpages for some functions and youo'll see. Having less boilerplate code to check for errors makes the code simpler to read, which improves the maintainability, and causes less bugs.

Features


Another point is the whole bunch of collection types GLib provides and that you don't have to reimplement. Just because reimplementing a linked list is easy doesn't mean you should do it. I worked at another company that shipped code with several linked list implementations, because some developpers would just have the Not Invented Here syndrome and would redevelop their own. A common, thouroughly tested, widespread library like GLib helps avoiding this nonsense. You shouldn't redevelop that stuff unless you have very specific performance constraints.


Their behavior is well-defined on any platform that GTK+ supports, as opposed to the native functions which may perhaps sometimes partway work.


I have to say, this is well intended but not well executed. It is sorta nice that your program won't crash and burn when you try to free() a pointer more than once. Or sort a NULL string. But that's a mixed blessing. It prevents you from discovering these nasty bugs in your code as well. Not only will you have a hard time getting your program ported some day, because you're relying on non-standard functions, you'll have a really hard time because your code was buggy and you never found out.


10 years ago, using the Gnome lib may have made sense, but its now a legacy liability. C89 is arguably the most standard language in the world, with very stable features and syntax, so debugging someone else's C89 code is doable.

By contrast Gnome's glib changes it's function's features outside of the C Standard, so not only do you get to deal with debugging obscure wrapper code made of C, but your code may stop working because Gnome changes it's wrapper functions.

Exhibit A: g_snprintf()

A safer form of the standard sprintf() function. The output is guaranteed to not exceed n characters (including the terminating nul character), so it is easy to ensure that a buffer overflow cannot occur.

See also g_strdup_printf().

In versions of GLib prior to 1.2.3, this function may return -1 if the output was truncated, and the truncated string may not be nul-terminated. In versions prior to 1.3.12, this function returns the length of the output string.

The return value of g_snprintf() conforms to the snprintf() function as standardized in ISO C99. Note that this is different from traditional snprintf(), which returns the length of the output string.

The format string may contain positional parameters, as specified in the Single Unix Specification.

I'm less than thrilled I get to write (yet another) linked-list to replace Gnome's, AND yet another version of snprintf() and a bunch of crappy wrapper code that silently malloc()s memory, thereby breaking the one absolute maxium of C coding: "Always malloc() and free() in the same scope" to replace g_strdup_printf().

g_strdup_printf ()

Similar to the standard C sprintf() function but safer, since it calculates the maximum space required and allocates memory to hold the result. The returned string should be freed with g_free() when no longer needed.

Add to this the thrill of making massive numbers of string changes in the code to do "useful" things like change gchar to char, gint to int, gboolean to bool, etc, etc, etc, ad nauseam until my Subversion comparisons are now a phone book. Worse, you end up having to change more and more code because this stuff is littered all over the .h files, so it keeps expanding, like a boated corpse, into a huge mess.

If you're scoping a contract job and see glib.h anywhere, RUN!!! Just say NO!.

PS: Downloading the source, removing all the Gnome-specific types, and recompiling it to make your own "g_"-less functions sorta, kinda works, and is a big time-saver.

Exhibit B: g_strdup_printf()

More horrific Gnome crappola from Gnome. Gnome has lots of "lovely" functions like g_strdup_vprintf() that "magically" know how much storage you need to hold your returned string, and I had occasion to look behind the "magic". This wins my award for the most hideous abuse of C ever.

If you keep tracing g_strdup_vprintf() back through all the wrapper functions, you come to this gem in gmessages.c....

/**
 * g_printf_string_upper_bound:
 * @format: the format string. See the printf() documentation
 * @args: the parameters to be inserted into the format string
 *
 * Calculates the maximum space needed to store the output
 * of the sprintf() function.
 *
 * Returns: the maximum space needed to store the formatted string
 */
gsize
g_printf_string_upper_bound (const gchar *format,
                             va_list      args)
{
  gchar c;
  return _g_vsnprintf (&c, 1, format, args) + 1;
}

Not only is ANY printf() function slower than snot at absolute zero, you'll notice they allocate an entire byte, yup, a whole gchar c, for storage, which guarantees overflow, but then who cares? they get the length of the string they'll need to malloc() - because they are going to turn around and do the whole printf() over again - this time, "magically" with just enough storage.

They'll add +1 to the size, of course, so you'll have room for a nul-terminator, guaranteed, at horrific expense of course, but they've hid it so deep in the code they're betting you'll just give up and use it blindly and not notice what a massive brain-fart this is. Gee, thanks guys. I just love my code to crawl.

Don't let the _g_vsnprintf() function throw you off, because in gprintfint.h you'll discover that little gem is just another name for plain old vanilla vsnprintf();

/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GLib Team and others 2002.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

#ifndef __G_PRINTFINT_H__
#define __G_PRINTFINT_H__

#ifdef HAVE_GOOD_PRINTF

#define _g_printf    printf
#define _g_fprintf   fprintf
#define _g_sprintf   sprintf
#define _g_snprintf  snprintf

#define _g_vprintf   vprintf
#define _g_vfprintf  vfprintf
#define _g_vsprintf  vsprintf
#define _g_vsnprintf vsnprintf

#else

#include "gnulib/printf.h"

#define _g_printf    _g_gnulib_printf
#define _g_fprintf   _g_gnulib_fprintf
#define _g_sprintf   _g_gnulib_sprintf
#define _g_snprintf  _g_gnulib_snprintf

#define _g_vprintf   _g_gnulib_vprintf
#define _g_vfprintf  _g_gnulib_vfprintf
#define _g_vsprintf  _g_gnulib_vsprintf
#define _g_vsnprintf _g_gnulib_vsnprintf

#endif

#endif /* __G_PRINTF_H__ */

It's highly recommended that you watch the Wizard Of Oz again before working with Gnome, so you'll know not to look behind the curtain. Welcome to my nightmare!

Anyone that thinks Gnome is more stable than C is badly lacking in critical thinking. You trade performance and transparency for a few nice things that are done better in the STL.


Here is an update. It looks the developer's realized their mistake:

g_mem_is_system_malloc has been deprecated since version 2.46 and should not be used in newly-written code.

GLib always uses the system malloc, so this function always returns TRUE.

Checks whether the allocator used by g_malloc() is the system's malloc implementation. If it returns TRUE memory allocated with malloc() can be used interchangeable with memory allocated using g_malloc(). This function is useful for avoiding an extra copy of allocated memory returned by a non-GLib-based API.

https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#g-mem-is-system-malloc

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜