string: Make g_string_free (_, FALSE) warn on unused result

...much like g_string_free_and_steal () does; by redirecting
g_string_free (_, FALSE) calls (when we can detect them) to
g_string_free_and_steal ().

This relies on some unpretty macros, but should be entirely transparent
to any users of g_string_free (). In particular, the macro only
evaluates its arguments once, no matter which branch ends up being
taken. The ternary operator the macro expands to always gets optimized
out, even at -O0: there is only one call to either g_string_free () or
g_string_free_and_steal () in the compiled code, with no run-time
branching.

Add a test for ensuring this works as expected in C++ too.

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
This commit is contained in:
Sergey Bugaev 2023-01-24 19:15:00 +03:00
parent bccff754b6
commit c3d07a625a
3 changed files with 47 additions and 4 deletions

View File

@ -206,8 +206,8 @@ g_string_new_len (const gchar *init,
* (i.e. %NULL if @free_segment is %TRUE)
*/
gchar *
g_string_free (GString *string,
gboolean free_segment)
(g_string_free) (GString *string,
gboolean free_segment)
{
gchar *segment;
@ -242,7 +242,7 @@ g_string_free (GString *string,
gchar *
g_string_free_and_steal (GString *string)
{
return g_string_free (string, FALSE);
return (g_string_free) (string, FALSE);
}
/**

View File

@ -57,10 +57,23 @@ GString* g_string_new_len (const gchar *init,
GLIB_AVAILABLE_IN_ALL
GString* g_string_sized_new (gsize dfl_size);
GLIB_AVAILABLE_IN_ALL
gchar* g_string_free (GString *string,
gchar* (g_string_free) (GString *string,
gboolean free_segment);
GLIB_AVAILABLE_IN_2_76
gchar* g_string_free_and_steal (GString *string) G_GNUC_WARN_UNUSED_RESULT;
#if G_GNUC_CHECK_VERSION (2, 0) && (GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_76)
#define g_string_free(str, free_segment) \
(__builtin_constant_p (free_segment) ? \
((free_segment) ? \
(g_string_free) ((str), (free_segment)) : \
g_string_free_and_steal (str)) \
: \
(g_string_free) ((str), (free_segment)))
#endif /* G_GNUC_CHECK_VERSION (2, 0) && (GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_76) */
GLIB_AVAILABLE_IN_2_34
GBytes* g_string_free_to_bytes (GString *string);
GLIB_AVAILABLE_IN_ALL

View File

@ -472,6 +472,35 @@ test_string_append (void)
g_string_free (string, TRUE);
}
static void
test_string_free (void)
{
GString *str;
gchar *data;
g_test_message ("Test that g_string_free() macro compiles and doesnt "
"cause any compiler warnings in C++ mode");
/* Test that g_string_free (_, TRUE) does not cause a warning if
* its return value is unused. */
str = g_string_new ("test");
g_string_free (str, TRUE);
/* Test that g_string_free (_, FALSE) does not emit a warning if
* its return value is used. */
str = g_string_new ("test");
data = g_string_free (str, FALSE);
g_free (data);
/* Test that g_string_free () with an expression that is always FALSE
* at runtime, but the compiler can't know it, does not cause any
* warnings if its return value is unused. */
str = g_string_new ("test");
data = str->str;
g_string_free (str, g_test_get_path ()[0] == 0);
g_free (data);
}
int
main (int argc, char *argv[])
{
@ -502,6 +531,7 @@ main (int argc, char *argv[])
g_test_add_func ("/C++/str-has-suffix", test_str_has_suffix);
g_test_add_func ("/C++/str-has-suffix/macro", test_str_has_suffix_macro);
g_test_add_func ("/C++/string-append", test_string_append);
g_test_add_func ("/C++/string-free", test_string_free);
return g_test_run ();
}