strfuncs: add g_set_str()

This is like our other suite of g_set_*() based APIs to simplify and
improve correctness of setters for fields, properties, and more.

This implementation specifically handles setting string values that may
point to an offset within the current string by copying before free.

strcmp() is used directly (as opposed to g_strcmp0() due to it being in
gtestutils.h as well as to increase the chance that the compiler will
hoist the implementation.

Fixes #2747
This commit is contained in:
Christian Hergert 2022-09-29 11:57:53 -07:00 committed by Philip Withnall
parent 96d623843f
commit 49ae9b490d
3 changed files with 76 additions and 0 deletions

View File

@ -1464,6 +1464,7 @@ utimbuf
<TITLE>String Utility Functions</TITLE>
<FILE>string_utils</FILE>
<INCLUDE>glib.h,glib/gprintf.h</INCLUDE>
g_set_str
g_strdup
g_strndup
g_strdupv

View File

@ -32,9 +32,11 @@
#endif
#include <stdarg.h>
#include <string.h>
#include <glib/gmacros.h>
#include <glib/gtypes.h>
#include <glib/gerror.h>
#include <glib/gmem.h>
G_BEGIN_DECLS
@ -363,6 +365,51 @@ gboolean g_ascii_string_to_unsigned (const gchar *str,
guint64 *out_num,
GError **error);
/**
* g_set_str: (skip)
* @str_pointer: (inout) (not optional) (nullable): a pointer to either a string or %NULL
* @new_str: (nullable): a string to assign to @str_pointer, or %NULL
*
* Updates a pointer to a string to a copy of @new_str. The previous string
* pointed to by @str_pointer will be freed with g_free().
*
* @str_pointer must not be %NULL, but can point to a %NULL value.
*
* One convenient usage of this function is in implementing property settings:
* |[
* void
* foo_set_bar (Foo *foo,
* const char *new_bar)
* {
* g_return_if_fail (IS_FOO (foo));
*
* if (g_set_str (&foo->bar, new_bar))
* g_object_notify (foo, "bar");
* }
* ]|
*
* Returns: %TRUE if the value of @str_pointer changed, %FALSE otherwise
*
* Since: 2.76
*/
GLIB_AVAILABLE_STATIC_INLINE_IN_2_76
static inline gboolean
g_set_str (char **str_pointer,
const char *new_str)
{
char *copy;
if (*str_pointer == new_str ||
(*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0))
return FALSE;
copy = g_strdup (new_str);
g_free (*str_pointer);
*str_pointer = copy;
return TRUE;
}
G_END_DECLS
#endif /* __G_STRFUNCS_H__ */

View File

@ -2546,6 +2546,33 @@ test_ascii_string_to_number_pathological (void)
g_assert_cmpint (svalue, ==, G_MININT64);
}
static void
test_set_str (void)
{
char *str = NULL;
g_assert_false (g_set_str (&str, NULL));
g_assert_null (str);
g_assert_true (g_set_str (&str, ""));
g_assert_false (g_set_str (&str, ""));
g_assert_nonnull (str);
g_assert_true ((gpointer)str != (gpointer)"");
g_assert_cmpstr (str, ==, "");
g_assert_true (g_set_str (&str, NULL));
g_assert_null (str);
g_assert_true (g_set_str (&str, ""));
g_assert_true (g_set_str (&str, "test"));
g_assert_cmpstr (str, ==, "test");
g_assert_true (g_set_str (&str, &str[2]));
g_assert_cmpstr (str, ==, "st");
g_free (str);
}
int
main (int argc,
char *argv[])
@ -2563,6 +2590,7 @@ main (int argc,
g_test_add_func ("/strfuncs/has-suffix", test_has_suffix);
g_test_add_func ("/strfuncs/memdup", test_memdup);
g_test_add_func ("/strfuncs/memdup2", test_memdup2);
g_test_add_func ("/strfuncs/set_str", test_set_str);
g_test_add_func ("/strfuncs/stpcpy", test_stpcpy);
g_test_add_func ("/strfuncs/str_match_string", test_str_match_string);
g_test_add_func ("/strfuncs/str_tokenize_and_fold", test_str_tokenize_and_fold);