+/* This is a skip variant of G_VALUE_LCOPY, same as there is
+ * G_VALUE_COLLECT_SKIP for G_VALUE_COLLECT.
+ */
+#define VALUE_LCOPY_SKIP(value_type, var_args) \
+ G_STMT_START { \
+ GTypeValueTable *_vtable = g_type_value_table_peek (value_type); \
+ const gchar *_lcopy_format = _vtable->lcopy_format; \
+ \
+ while (*_lcopy_format) { \
+ switch (*_lcopy_format++) { \
+ case G_VALUE_COLLECT_INT: \
+ va_arg ((var_args), gint); \
+ break; \
+ case G_VALUE_COLLECT_LONG: \
+ va_arg ((var_args), glong); \
+ break; \
+ case G_VALUE_COLLECT_INT64: \
+ va_arg ((var_args), gint64); \
+ break; \
+ case G_VALUE_COLLECT_DOUBLE: \
+ va_arg ((var_args), gdouble); \
+ break; \
+ case G_VALUE_COLLECT_POINTER: \
+ va_arg ((var_args), gpointer); \
+ break; \
+ default: \
+ g_assert_not_reached (); \
+ } \
+ } \
+ } G_STMT_END
+
+/* Initializes hash table to hold arg names as keys and GValues of
+ * given type, but without any specific value. Note that if you are
+ * going to use OUT_HASH_TABLE_TO_VAR_ARGS then you have to store a
+ * copy of var_args with G_VA_COPY before using this macro.
+ */
+#define VAR_ARGS_TO_OUT_HASH_TABLE(var_args, hash) \
+ G_STMT_START { \
+ const gchar *arg_name = va_arg (var_args, const gchar *); \
+ \
+ while (arg_name != NULL) { \
+ GValue *value = g_new0 (GValue, 1); \
+ GType type = va_arg (var_args, GType); \
+ \
+ VALUE_LCOPY_SKIP (type, var_args); \
+ g_value_init (value, type); \
+ g_hash_table_insert (hash, g_strdup (arg_name), value); \
+ arg_name = va_arg (var_args, const gchar *); \
+ } \
+ } G_STMT_END
+
+/* Initializes hash table to hold arg names as keys and GValues of
+ * given type and value.
+ */
+#define VAR_ARGS_TO_IN_HASH_TABLE(var_args, hash) \
+ G_STMT_START { \
+ const gchar *arg_name = va_arg (var_args, const gchar *); \
+ \
+ while (arg_name != NULL) { \
+ GValue *value = g_new0 (GValue, 1); \
+ gchar *error = NULL; \
+ GType type = va_arg (var_args, GType); \
+ \
+ G_VALUE_COLLECT_INIT (value, \
+ type, \
+ var_args, \
+ G_VALUE_NOCOPY_CONTENTS, \
+ &error); \
+ if (error == NULL) { \
+ g_hash_table_insert (hash, g_strdup (arg_name), value); \
+ } else { \
+ g_warning ("Failed to collect value of type %s for %s: %s", \
+ g_type_name (type), \
+ arg_name, \
+ error); \
+ g_free (error); \
+ } \
+ arg_name = va_arg (var_args, const gchar *); \
+ } \
+ } G_STMT_END
+
+/* Puts values stored in hash table with GValues into var args.
+ */
+#define OUT_HASH_TABLE_TO_VAR_ARGS(hash, var_args) \
+ G_STMT_START { \
+ const gchar *arg_name = va_arg (var_args, const gchar *); \
+ \
+ while (arg_name != NULL) { \
+ GValue *value = g_hash_table_lookup (hash, arg_name); \
+ GType type = va_arg (var_args, GType); \
+ \
+ if (value == NULL) { \
+ g_warning ("No value for %s", arg_name); \
+ G_VALUE_COLLECT_SKIP (type, var_args); \
+ } else if (G_VALUE_TYPE (value) != type) { \
+ g_warning ("Different GType in value (%s) and in var args (%s) for %s.", \
+ G_VALUE_TYPE_NAME (value), \
+ g_type_name (type), \
+ arg_name); \
+ } else { \
+ gchar *error = NULL; \
+ \
+ G_VALUE_LCOPY (value, var_args, 0, &error); \
+ if (error != NULL) { \
+ g_warning ("Failed to lcopy the value of type %s for %s: %s", \
+ g_type_name (type), \
+ arg_name, \
+ error); \
+ g_free (error); \
+ } \
+ } \
+ arg_name = va_arg (var_args, const gchar *); \
+ } \
+ } G_STMT_END
+
+/* GDestroyNotify for GHashTable holding GValues.
+ */
+static void
+value_free (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_free (value);
+}
+