Coding Guidelines/String Functions: Difference between revisions

From wiki.zmanda.com
Jump to navigation Jump to search
(vstrextend)
No edit summary
Line 1: Line 1:
Amanda provides a library of string functions with names that parse like this:
== In general ==
new|v|stralloc|f
as well as a few others (described below).  The entire set of functions appears in <tt>amanda.h</tt>.  The functions are all implemented as macros to debug functions, to allow tracing of file/line numbers.


The base function is, of course, ''stralloc'', which simply allocates enough space, and then copies the string it is given as an argument.  Adding ''new'' means that the first argument is a dynamically allocated string which this function will be '''replacing''': if it is not NULL, it will be freed after the new string is allocated and constructed.  This allows code like
Amanda uses GLib to manage strings. You should, in particular, see these pages:
{
  char *tmp = NULL;
  tmp = newstralloc(tmp, "first string");
  /* ... */
  tmp = newstralloc(tmp, "second string");
  /* ... */
  amfree(tmp);
}
without worrying about memory leaks (since each ''newstralloc'' invocation will ''free(tmp)'' if it is non-NULL).


Adding ''v'' to a function name indicates that it is '''variadic''' - that is, that it takes a variable number of arguments. Variadic functions which end in ''f'' act like ''printf'', with the format as the first (or second, in the case of ''new*'' functions) argument. Variadic functions without ''f'' must be given a NULL as the final argument.  Examples:
* [http://wiki.zmanda.com/glib-docs/glib/glib-String-Utility-Functions.html string utility functions];
tmp = newvstrallocf(tmp, "could not open '%s': %s", filename, strerror(errno));
* [http://wiki.zmanda.com/glib-docs/glib/glib-Strings.html the GString data type];
*errmsg = vstralloc(TMPDIR, "/foo/", filename, NULL);
* [http://wiki.zmanda.com/glib-docs/glib/glib-Pointer-Arrays.html the GPtrArray data type].


= Misc Functions =
== Examples: string utility functions ==
The function ''stralloc2'' is a simple concatenation option; ''stralloc2(x, y)'' is equivalent to ''vstralloc(x, y, NULL)''.


The function ''strappend'' is a macro which appends its second argument to its first.  ''strappend(x, y)'' is equivalent to ''newvstralloc(x, x, y, NULL)''.
GLib's string utilities contain a lot of very convenient functions, for example to dynamically allocate strings. Examples:


The function ''vstrextend'' is like ''strappend'', but allows multiple additional strings to be specified; its argument list must end in NULL, e.g.,
<pre>
vstrextend(mystr, "Oh, I forgot to mention ...\n", "And one other thing ...\n", NULL);
/* Duplicate a plain string */
char *p = g_strdup("Hello world");
char *p = g_strdup(othervariablehere);
/* Allocate a directly formatted string */
char *p = g_strdup_printf("Hello %s", world);
/* Split a string against a separator */
gchar **strings = g_strsplit("\n", othervariablethere);
</pre>
 
You should also favor GLib's versions of tolower/toupper, specifically the g_ascii_*() functions, since they are insensitive to the locale. Example:
 
<pre>
/* Duplicate a string to a lower version of it */
char *low = g_ascii_tolower("HeLLo wOrlD"); /* low will be "hello world" */
</pre>
 
Finally, use <tt>g_free()</tt> to free the strings.
 
More generally, if GLib has a replacement function to a C standard, you <i>should</i> use it.
 
== Example: GString ==
 
GString is the base string buffer to which you can append/allocate etc. Its usage is quite simple, however you must pay attention to what you want to do when finished with the object. Either you want to get the stored string back, or you don't:
 
<pre>
GString *strbuf = g_string_new(NULL); /* This will spawn an empty string */
g_string_append(strbuf, "some string");
g_string_append_printf(strbuf, "Hello %s", "Mars");
g_string_append_c(strbuf, '\n');
/* g_string_prepend(), etc etc - see the link above */
/*
* Getting the string stored in it while discarding the GString:
*/
char *p = g_string_free(strbuf, FALSE);
/*
* Completely scratching the buffer, including its contents:
*/
g_string_free(strbuf, TRUE);
</pre>
 
== Example: GPtrArray ==
 
While this data type can store ANY type of data, it is hugely convenient for separator-based string buildings. For instance, here is how to build a space-separated string of a command line received as an argument:
 
<pre>
#include <stdlib.h>
#include <glib.h>
 
int main(int argc, char **argv)
{
    int i;
    GPtrArray *array = g_ptr_array_new();
    gchar **strings;
    char *result;
 
    for (i = 0; i < argc; i++)
        g_ptr_array_add(array, argv[i]);
 
    /* NEVER FORGET THAT! */
    g_ptr_array_add(array, NULL);
 
    strings = (gchar **)g_ptr_array_free(array, FALSE);
 
    p = g_strjoinv(" ", strings);
   
    /*
    * BEWARE here: if your GPtrArray contains elements of dynamically allocated strings,
    * then you should use g_strfreev(). BUT if the pointers contain only statically
    * allocated strings, you MUST use g_free().
    */
 
    g_free(strings);
 
    g_fprintf(stderr, "My command line was: \"%s\"\n", p);
 
    g_free(p);
 
    exit(0);
}
</pre>
 
Some more hints:
* g_strjoinv() will, by default, NOT put the separator after the last line. If this is what you want, add the empty string (either "" or g_strdup("") to the end of the array before adding NULL;
* ENSURE that your array contains either statically allocated strings or dynamically allocated strings, but NEVER both mixed!
 
Here is another example which duplicated all of its inputs. Look at the differences:
 
 
<pre>
#include <stdlib.h>
#include <glib.h>
 
int main(int argc, char **argv)
{
    int i;
    GPtrArray *array = g_ptr_array_new();
    gchar **strings;
    char *result;
 
    for (i = 0; i < argc; i++)
        g_ptr_array_add(array, g_strdup(argv[i]));
 
    /* NEVER FORGET THAT! */
    g_ptr_array_add(array, NULL);
 
    strings = (gchar **)g_ptr_array_free(array, FALSE);
 
    p = g_strjoinv(" ", strings);
   
    /*
    * Note: g_strfreev(), not g_free()!
    */
 
    g_strfreev(strings);
 
    g_fprintf(stderr, "My command line was: \"%s\"\n", p);
 
    g_free(p);
 
    exit(0);
}
</pre>

Revision as of 11:42, 3 June 2011

In general

Amanda uses GLib to manage strings. You should, in particular, see these pages:

Examples: string utility functions

GLib's string utilities contain a lot of very convenient functions, for example to dynamically allocate strings. Examples:

/* Duplicate a plain string */
char *p = g_strdup("Hello world");
char *p = g_strdup(othervariablehere);
/* Allocate a directly formatted string */
char *p = g_strdup_printf("Hello %s", world);
/* Split a string against a separator */
gchar **strings = g_strsplit("\n", othervariablethere);

You should also favor GLib's versions of tolower/toupper, specifically the g_ascii_*() functions, since they are insensitive to the locale. Example:

/* Duplicate a string to a lower version of it */
char *low = g_ascii_tolower("HeLLo wOrlD"); /* low will be "hello world" */

Finally, use g_free() to free the strings.

More generally, if GLib has a replacement function to a C standard, you should use it.

Example: GString

GString is the base string buffer to which you can append/allocate etc. Its usage is quite simple, however you must pay attention to what you want to do when finished with the object. Either you want to get the stored string back, or you don't:

GString *strbuf = g_string_new(NULL); /* This will spawn an empty string */
g_string_append(strbuf, "some string");
g_string_append_printf(strbuf, "Hello %s", "Mars");
g_string_append_c(strbuf, '\n');
/* g_string_prepend(), etc etc - see the link above */
/*
 * Getting the string stored in it while discarding the GString:
 */
char *p = g_string_free(strbuf, FALSE);
/*
 * Completely scratching the buffer, including its contents:
 */
g_string_free(strbuf, TRUE);

Example: GPtrArray

While this data type can store ANY type of data, it is hugely convenient for separator-based string buildings. For instance, here is how to build a space-separated string of a command line received as an argument:

#include <stdlib.h>
#include <glib.h>

int main(int argc, char **argv)
{
    int i;
    GPtrArray *array = g_ptr_array_new();
    gchar **strings;
    char *result;

    for (i = 0; i < argc; i++)
        g_ptr_array_add(array, argv[i]);

    /* NEVER FORGET THAT! */
    g_ptr_array_add(array, NULL);

    strings = (gchar **)g_ptr_array_free(array, FALSE);

    p = g_strjoinv(" ", strings);
    
    /*
     * BEWARE here: if your GPtrArray contains elements of dynamically allocated strings,
     * then you should use g_strfreev(). BUT if the pointers contain only statically
     * allocated strings, you MUST use g_free().
     */

    g_free(strings);

    g_fprintf(stderr, "My command line was: \"%s\"\n", p);

    g_free(p);

    exit(0);
}

Some more hints:

  • g_strjoinv() will, by default, NOT put the separator after the last line. If this is what you want, add the empty string (either "" or g_strdup("") to the end of the array before adding NULL;
  • ENSURE that your array contains either statically allocated strings or dynamically allocated strings, but NEVER both mixed!

Here is another example which duplicated all of its inputs. Look at the differences:


#include <stdlib.h>
#include <glib.h>

int main(int argc, char **argv)
{
    int i;
    GPtrArray *array = g_ptr_array_new();
    gchar **strings;
    char *result;

    for (i = 0; i < argc; i++)
        g_ptr_array_add(array, g_strdup(argv[i]));

    /* NEVER FORGET THAT! */
    g_ptr_array_add(array, NULL);

    strings = (gchar **)g_ptr_array_free(array, FALSE);

    p = g_strjoinv(" ", strings);
    
    /*
     * Note: g_strfreev(), not g_free()!
     */

    g_strfreev(strings);

    g_fprintf(stderr, "My command line was: \"%s\"\n", p);

    g_free(p);

    exit(0);
}