📄 book.html
字号:
<p> There are also many macros unique to glib, such as the portable <tt>gpointer</tt>-to-<tt>gint</tt> and <tt>gpointer</tt>-to-<tt>guint</tt> conversions shown in Macro Listing 2.2.</p> <p> Most of glib's data structures are designed to store a <tt>gpointer</tt>. If you want to store pointers to dynamically allocated objects, this is the right thing. However, sometimes you want to store a simple list of integers without having to dynamically allocate them. Though the C standard does not strictly guarantee it, it is possible to store a <tt>gint</tt> or <tt>guint</tt> in a <tt>gpointer</tt> variable on the wide range of platforms glib has been ported to; in some cases, an intermediate cast is required. The macros in Macro Listing 2.2 abstract the presence of the cast.</p> <p> Here's an example:</p> <pre> gint my_int; gpointer my_pointer; my_int = 5; my_pointer = GINT_TO_POINTER(my_int); printf("We are storing %d\n", GPOINTER_TO_INT(my_pointer));</pre><p> Be careful, though; these macros allow you to store an integer in a pointer, but storing a pointer in an integer will <i>not</i> work. To do that portably, you must store the pointer in a <tt>long</tt>. (It's undoubtedly a bad idea to do so, however.)</p> <p><table bgcolor="#FFEEEE" width="100%"><caption><b>Macro Listing 2.2:</b> Macros for storing integers in pointers</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre><tt>GINT_TO_POINTER(p)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>GPOINTER_TO_INT(p)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>GUINT_TO_POINTER(p)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>GPOINTER_TO_UINT(p)</tt></pre></font> </td></tr></table></p><h3>2.1.3: Debugging Macros</h3><p> glib has a nice set of macros you can use to enforce invariants and preconditions in your code. Gtk+ uses these liberally---one of the reasons it's so stable and easy to use. They all disappear when you define <tt>G_DISABLE_CHECKS</tt> or <tt>G_DISABLE_ASSERT</tt>, so there's no performance penalty in production code. Using these liberally is a very, very good idea. You'll find bugs much faster if you do. You can even add assertions and checks whenever you find a bug to be sure the bug doesn't reappear in future versions---this complements a regression suite. Checks are especially useful when the code you're writing will be used as a black box by other programmers; users will immediately know when and how they've misused your code.</p> <p> Of course you should be very careful to ensure your code isn't subtly dependent on debug-only statements to function correctly. Statements that will disappear in production code should <i>never</i> have side effects.</p> <p><table bgcolor="#FFEEEE" width="100%"><caption><b>Macro Listing 2.3:</b> Precondition Checks</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre><tt>g_return_if_fail(condition)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>g_return_val_if_fail(condition, retval)</tt></pre></font> </td></tr></table></p><p> Macro Listing 2.3 shows glib's precondition checks. <tt>g_return_if_fail()</tt> prints a warning and immediately returns from the current function if <tt>condition</tt> is <tt>FALSE</tt>. <tt>g_return_val_if_fail()</tt> is similar but allows you to return some <tt>retval</tt>. These macros are incredibly useful---if you use them liberally, especially in combination with Gtk+'s runtime type checking, you'll halve the time you spend looking for bad pointers and type errors.</p> <p> Using these functions is simple; here's an example from the glib hash table implementation:</p> <pre>voidg_hash_table_foreach (GHashTable *hash_table, GHFunc func, gpointer user_data){ GHashNode *node; gint i; g_return_if_fail (hash_table != NULL); g_return_if_fail (func != NULL); for (i = 0; i < hash_table->size; i++) for (node = hash_table->nodes[i]; node; node = node->next) (* func) (node->key, node->value, user_data);}</pre><p> Without the checks, passing <tt>NULL</tt> as a parameter to this function would result in a mysterious segmentation fault. The person using the library would have to figure out where the error occurred with a debugger, and maybe even dig in to the glib code to see what was wrong. With the checks, they'll get a nice error message telling them that <tt>NULL</tt> arguments are not allowed.</p> <p><table bgcolor="#FFEEEE" width="100%"><caption><b>Macro Listing 2.4:</b> Assertions</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre><tt>g_assert(condition)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>g_assert_not_reached()</tt></pre></font> </td></tr></table></p><p> glib also has more traditional assertion macros, shown in Macro Listing 2.4. <tt>g_assert()</tt> is basically identical to <tt>assert()</tt>, but responds to <tt>G_DISABLE_ASSERT</tt> and behaves consistently across all platforms. <tt>g_assert_not_reached()</tt> is also provided; this is an assertion which always fails. Assertions call <tt>abort()</tt> to exit the program and (if your environment supports it) dump a core file for debugging purposes.</p> <p> Fatal assertions should be used to check <i>internal consistency</i> of a function or library, while <tt>g_return_if_fail()</tt> is intended to ensure sane values are passed to the public interfaces of a program module. That is, if an assertion fails, you typically look for a bug in the module containing the assertion; if a <tt>g_return_if_fail()</tt> check fails, you typically look for the bug in the code which invokes the module.</p> <p> This code from glib's calendrical calculations module shows the difference:</p> <pre>GDate*g_date_new_dmy (GDateDay day, GDateMonth m, GDateYear y){ GDate *d; g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL); d = g_new (GDate, 1); d->julian = FALSE; d->dmy = TRUE; d->month = m; d->day = day; d->year = y; g_assert (g_date_valid (d)); return d;}</pre><p> The precondition check at the beginning ensures the user passes in reasonable values for the day, month and year; the assertion at the end ensures that glib constructed a sane object, given sane values.</p> <p> <tt>g_assert_not_reached()</tt> should be used to mark "impossible" situations; a common use is to detect switch statements that don't handle all possible values of an enumeration:</p> <pre> switch (val) { case FOO_ONE: break; case FOO_TWO: break; default: /* Invalid enumeration value */ g_assert_not_reached(); break; }</pre><p> All of the debugging macros print a warning using glib's <tt>g_log()</tt> facility, which means the warning includes the name of the originating application or library, and you can optionally install a replacement warning-printing routine. For example, you might send all warnings to a dialog box or log file instead of printing them on the console.</p> <h3>2.1.4: Memory</h3><p> glib wraps the standard <tt>malloc()</tt> and <tt>free()</tt> with its own <tt>g_</tt> variants, <tt>g_malloc()</tt> and <tt>g_free()</tt>, shown in Function Listing 2.1. These are nice in several small ways:</p> <ul><li> <tt>g_malloc()</tt> always returns a <tt>gpointer</tt>, never a <tt>char*</tt>, so there's no need to cast the return value.<li> <tt>g_malloc()</tt> aborts the program if the underlying <tt>malloc()</tt> fails, so you don't have to check for a <tt>NULL</tt> return value.<li> <tt>g_malloc()</tt> gracefully handles a <tt>size</tt> of <tt>0</tt>, by returning <tt>NULL</tt>.<li> <tt>g_free()</tt> will ignore any <tt>NULL</tt> pointers you pass to it.</ul><p> In addition to these minor conveniences, <tt>g_malloc()</tt> and <tt>g_free()</tt> can support various kinds of memory debugging and profiling. If you pass the <tt>--enable-mem-check</tt> option to glib's configure script, the compiled <tt>g_free()</tt> will warn you whenever you free the same pointer twice. The <tt>--enable-mem-profile</tt> option enables code which keeps memory use statistics; when you call <tt>g_mem_profile()</tt> they are printed to the console. Finally, you can define <tt>USE_DMALLOC</tt> and the glib memory wrappers will use the <tt>MALLOC()</tt>, etc. debugging macros available in <tt>dmalloc.h</tt> on some platforms.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.1:</b> glib memory allocation</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>gpointerg_malloc(gulong size)</pre></font> </td></tr><tr><td> <font color="black"><pre>voidg_free(gpointer mem)</pre></font> </td></tr><tr><td> <font color="black"><pre>gpointerg_realloc(gpointer mem, gulong size)</pre></font> </td></tr><tr><td> <font color="black"><pre>gpointerg_memdup(gconstpointer mem, guint bytesize)</pre></font> </td></tr></table></p><p> It's important to match <tt>g_malloc()</tt> with <tt>g_free()</tt>, plain <tt>malloc()</tt> with <tt>free()</tt>, and (if you're using C++) <tt>new</tt> with <tt>delete</tt>. Otherwise bad things can happen, since these allocators may use different memory pools (and <tt>new</tt>/<tt>delete</tt> call constructors and destructors).</p> <p> Of course there's a <tt>g_realloc()</tt> equivalent to <tt>realloc()</tt>. There's also a convenient <tt>g_malloc0()</tt> which fills allocated memory with 0s, and <tt>g_memdup()</tt> which returns a copy of <tt>bytesize</tt> bytes starting at <tt>mem</tt>. <tt>g_realloc()</tt> and <tt>g_malloc0()</tt> will both accept a <tt>size</tt> of <tt>0</tt>, for consistency with <tt>g_malloc()</tt>. However, <tt>g_memdup()</tt> will not.</p> <p> If it isn't obvious: <tt>g_malloc0()</tt> fills raw memory with unset bits, not the value <tt>0</tt> for whatever type you intend to put there. Occasionally someone expects to get an array of floating point numbers initialized to <tt>0.0</tt>; this will <i>not</i> work.</p> <p> Finally, there are type-aware allocation macros, shown in Macro Listing 2.5. The <tt>type</tt> argument to each of these is the name of a type, and the <tt>count</tt> argument is the number of <tt>type</tt>-size blocks to allocate. These macros save you some typing and multiplication, and are thus less error-prone. They automatically cast to the target pointer type, so attempting to assign the allocated memory to the wrong kind of pointer should trigger a compiler warning. (If you have warnings turned on, as a responsible programmer should!)</p> <p><table bgcolor="#FFEEEE" width="100%"><caption><b>Macro Listing 2.5:</b> Allocation macros</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre><tt>g_new(type, count)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>g_new0(type, count)</tt></pre></font> </td></tr><tr><td> <font color="black"><pre><tt>g_renew(type, mem, count)</tt></pre></font> </td></tr></table></p><h3>2.1.5: String Handling</h3><p> glib provides a number of functions for string handling; some are unique to glib, and some solve portability concerns. They all interoperate nicely with the glib memory allocation routines.</p> <p> For those interested in a better string than <tt>gchar*</tt>, there's also a <tt>GString</tt> type. It isn't covered in this book, but documentation is available at <a href="http://www.gtk.org/">http://www.gtk.org/</a>.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.2:</b> Portability Wrappers</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>gintg_snprintf(gchar* buf, gulong n, const gchar* format, ...)</pre></font> </td></tr><tr><td> <font color="black"><pre>gintg_strcasecmp(const gchar* s1, const gchar* s2)</pre></font> </td></tr>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -