GVariant 格式字符串

GVariant 格式字符串

可变参数转换

本页尝试介绍如何使用 GVariant 来执行可变参数转换。

转换根据格式字符串进行。格式字符串是单个 GVariant 值与一个或多个 C 值之间的双向映射。

使用 g_variant_new() 函数将 C 值转换为 GVariant 值。使用 g_variant_get() 函数将 GVariant 转换为 C 值。

语法

本节详尽地描述了 GVariant 格式字符串的所有可能性。除了此处描述的内容之外,格式字符串不存在其他有效形式。请注意,格式字符串语法将来可能会扩展。

有效的格式字符串具有以下之一的 形式

  • 任意类型 字符串
  • @ 前缀的类型字符串
  • &s&o&g^as^a&s^ao^a&o^ay^&ay^aay^a&ay
  • 任意格式字符串,以 m 为前缀
  • 由零个或多个格式字符串组成、链接并用 括号括起来的序列
  • 一个开头大括号,后面是两个格式字符串,然后是一个结尾大括号(受约束的是,第一个格式字符串对应于有效的用于字典的键类型)

符号

下表描述了 GVariant 格式字符串中可能出现的符号的粗略含义。每个符号都在其自己的一节中进行了详细描述,包括使用 示例。

符号 含义
bynqiuxthd 用于构建或解构布尔、字节和数字类型。请参见下文的 数字类型
sog 用于构建或解构字符串类型。请参见下文的 字符串
v 用于构建或解构变体类型。请参见下文的 变体
a 用于构建或解构数组。请参见下文的 数组
m 用于构建或解构可能类型。请参见下文的 可能类型
() 用于构建或解构元组。请参见下文的 元组
{} 用于构建或解构字典项。请参见下文的 字典
@ 用作 GVariant 类型字符串的前缀(不是格式字符串的前缀,因此 as 是有效的格式字符串,但 @^as 不是)。表示应该使用指向 GVariant 的指针来代替常规 C 类型或类型。对于 g_variant_new(),这意味着你必须传递一个非 NULL(GVariant *);如果是浮动引用,则将采用所有权,就像使用 g_variant_ref_sink() 一样。对于 g_variant_get(),这意味着你必须传递一个指向 (GVariant *) 的指针,以便通过引用返回该值,或者传递 NULL 来忽略该值。请参见下文的 GVariant *
*?r @*@?r 完全等效。仅供完整性提供,以便所有 GVariant 类型字符串也可用作格式字符串。请参见下面的 GVariant *
& 用作 GVariant 类型字符串的前缀(不是格式字符串的前缀,因此 &s 是有效的格式字符串,但 &s“ 不是)。表示应该使用指向序列化数据的 C 指针,而不是正常的 C 类型。请参见下面的 指针
^ 用作某些特定类型格式字符串的前缀。请参见下面的 便捷转换

数值类型

字符:bynqiuxthd

从数值类型进行的可变参数转换以最明显的方式进行。遇到其中一个字符时,g_variant_new() 将等效的 C 类型作为参数。g_variant_get() 获取一个指针,该指针指向等效的 C 类型(或 NULL 以忽略值)。

等效的 C 类型如下

字符 等效的 C 类型
b gboolean
y guchar
n gint16
q guint16
i gint32
u guint32
x gint64
t guint64
h gint32(句柄)
d gdouble

请注意,在 C 中,可变参数列表中的小整数类型会根据需要提升到 intunsigned int,并相应地读回。GLib 当前支持的每个平台上的 int 都是 32 位。这意味着你可以对类型为 int 的 C 表达式与 g_variant_new() 和格式字符 bynqiuh 一起使用。具体来说,你可以对这些字符使用整数字面量。

当使用 xt 字符时,你必须确保提供的价值为 64 位。这意味着你应使用强制转换或使用 G_GINT64_CONSTANTG_GUINT64_CONSTANT 宏。

使用 g_variant_get() 时不会进行类型提升,因为它使用指针。指针必须始终指向确切合适大小的内存区域。

示例

GVariant *value1, *value2, *value3, *value4;

value1 = g_variant_new ("y", 200);
value2 = g_variant_new ("b", TRUE);
value3 = g_variant_new ("d", 37.5):
value4 = g_variant_new ("x", G_GINT64_CONSTANT (998877665544332211));

{
  gdouble floating;
  gboolean truth;
  gint64 bignum;


  g_variant_get (value1, "y", NULL);      /* ignore the value. */
  g_variant_get (value2, "b", &truth);
  g_variant_get (value3, "d", &floating);
  g_variant_get (value4, "x", &bignum);
}

字符串

字符:sog

字符串转换在标准空终止的 C 字符串之间进行。在格式字符串中遇到 sog 时,g_variant_new() 会获取 (const gchar *) 并复制一份。NULL 不是有效的字符串;使用可能类型对其进行编码。如果使用 og 字符,则必须注意确保传递的字符串分别是有效的 D-Bus 对象路径或 D-Bus 类型签名。

在遇到 sog 时,g_variant_get() 会获取一个指向 (gchar *)(即:(gchar **))的指针,并将其设置为字符串的新分配副本。使用 g_free() 释放此副本是适当的。还可以传递 NULL 以指示应忽略字符串的值(在这种情况下,不进行复制)。

示例

GVariant *value1, *value2, *value3;

value1 = g_variant_new ("s", "hello world!");
value2 = g_variant_new ("o", "/must/be/a/valid/path");
value3 = g_variant_new ("g", "iias");

#if 0
  g_variant_new ("s", NULL);      /* not valid: NULL is not a string. */
#endif

{
  gchar *result;

  g_variant_get (value1, "s", &result);
  g_print ("It was '%s'\n", result);
  g_free (result);
}

变体

字符:v

在遇到 v 时,g_variant_new() 会获取一个 (GVariant *)GVariant 的值用作变体值的内容。

在遇到一个v时,g_variant_get()将取用一个指向(GVariant *)(即:(GVariant **))的指针。它被设为一个包含变体值内容的GVariant实例的新引用。使用g_variant_unref()来释放此引用是合适的。还可以传递NULL来表示应忽略该值(在这种情况下,不会创建任何新引用)。

示例

GVariant *x, *y;

/* the following two lines are equivalent: */
x = g_variant_new ("v", y);
x = g_variant_new_variant (y);

/* as are these: */
g_variant_get (x, "v", &y);
y = g_variant_get_variant (x);

数组

字符:a

在遇到一个后面跟着类型字符串的a字符时,g_variant_new()将取用一个已创建为类型的数组的数组生成器的(GVariantBuilder *)。在生成器上将调用g_variant_builder_end()并将结果用作值。作为特例,如果给定的类型字符串是明确类型,则可以赋予NULL来表示该类型的空数组。

在遇到一个后面跟着类型字符串的a字符时,g_variant_get()将取用一个指向一个(GVariantIter *)(即:(GVariantIter **))的指针。将创建并返回一个新的堆分配的迭代器,它已初始化为对数组的元素进行迭代。使用g_variant_iter_free()在完成使用后应释放此迭代器。还可以赋予NULL来表示应忽略数组的值。

示例

GVariantBuilder *builder;
GVariant *value;

builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
g_variant_builder_add (builder, "s", "when");
g_variant_builder_add (builder, "s", "in");
g_variant_builder_add (builder, "s", "the");
g_variant_builder_add (builder, "s", "course");
value = g_variant_new ("as", builder);
g_variant_builder_unref (builder);

{
  GVariantIter *iter;
  gchar *str;

  g_variant_get (value, "as", &iter);
  while (g_variant_iter_loop (iter, "s", &str))
    g_print ("%s\n", str);
  g_variant_iter_free (iter);
}

g_variant_unref (value);

Maybe 类型

字符:m

Maybe 类型根据 m 后面的格式字符串以两种不同的方式进行处理。目前使用哪种方法完全取决于 m 后面的字符。

第一种方法适用于从 asogv@*?r&^ 开始的格式字符串。在所有这些情况下,对于非 Maybe 类型,g_variant_new() 取用一个指向非 NULL 值的指针,g_variant_get() 返回(通过引用)一个非 NULL 指针。当其中任何格式字符串的前缀为 m 时,收集的参数类型不会以任何方式改变,但 NULL 变为允许值,以表示 Nothing 情况。

请注意,在数组部分中为构建空数组而引入的“特例”在此处被忽略了。对使用格式字符串 mas NULL 指针的构造是一个 Nothing 值 - 而不是空数组。

第二种方法适用于所有其他格式字符串。对于 g_variant_new() 将收集一个额外的 gboolean 参数,对于 g_variant_get() 将收集一个额外的 (gboolean *)。在此参数之后,将收集为等效的非 Maybe 类型正常收集的参数。

如果向 g_variant_new() 传入 FALSE,则将构造 Nothing 值且将忽略收集的参数。否则(如果传入 TRUE),将按正常方式使用这些参数来创建 Just 值。

如果向 g_variant_get() 传递 NULL,则将忽略该值。如果传递一个非 NULL 指针,则会使用该指针通过引用返回该值是否为 Just。如果该值是 Just,则会将 gboolean 设置为 TRUE 并将该值按通常方式存储在参数中。如果该值是 Nothing,则会将 gboolean 设置为 FALSE 且将按正常方式收集这些参数,但会将它们的值设置为二进制零。

示例

GVariant *value1, *value2, *value3, *value4, *value5, *value6;
value1 = g_variant_new ("ms", "Hello world");
value2 = g_variant_new ("ms", NULL);
value3 = g_variant_new ("(m(ii)s)", TRUE, 123, 456, "Done");
value4 = g_variant_new ("(m(ii)s)", FALSE, -1, -1, "Done");          /* both '-1' are ignored. */
value5 = g_variant_new ("(m@(ii)s)", NULL, "Done");

{
  GVariant *contents;
  const gchar *cstr;
  gboolean just;
  gint32 x, y;
  gchar *str;

  g_variant_get (value1, "ms", &str);
  if (str != NULL)
    g_print ("str: %s\n", str);
  else
    g_print ("it was null\n");
  g_free (str);


  g_variant_get (value2, "m&s", &cstr);
  if (cstr != NULL)
    g_print ("str: %s\n", cstr);
  else
    g_print ("it was null\n");
  /* don't free 'cstr' */


  /* NULL passed for the gboolean *, but two 'gint32 *' still collected */
  g_variant_get (value3, "(m(ii)s)", NULL, NULL, NULL, &str);
  g_print ("string is %s\n", str);
  g_free (str);

  /* note: &s used, so g_free() not needed */
  g_variant_get (value4, "(m(ii)&s)", &just, &x, &y, &cstr);
  if (just)
    g_print ("it was (%d, %d)\n", x, y);
  else
    g_print ("it was null\n");
  g_print ("string is %s\n", cstr);
  /* don't free 'cstr' */


  g_variant_get (value5, "(m*s)", &contents, NULL); /* ignore the string. */
  if (contents != NULL)
    {
      g_variant_get (contents, "(ii)", &x, &y);
      g_print ("it was (%d, %d)\n", x, y);
      g_variant_unref (contents);
    }
  else
    g_print ("it was null\n");
}

元组

字符:()

从左到右处理元组中的每个项目,即可处理元组。每个项目按照惯例处理。

示例

GVariant *value1, *value2;

value1 = g_variant_new ("(s(ii))", "Hello", 55, 77);
value2 = g_variant_new ("()");

{
  gchar *string;
  gint x, y;

  g_variant_get (value1, "(s(ii))", &string, &x, &y);
  g_print ("%s, %d, %d\n", string, x, y);
  g_free (string);

  g_variant_get (value2, "()");   /* do nothing... */
}

GVariant *

字符:@*?r

遇到类型字符串前面的 @ 时,g_variant_new() 接受指向 GVariant 的非 NULL 指针,并直接使用其值,而不会收集参数创建值。提供的 GVariant 必须具有与 @ 之后的类型字符串匹配的类型。*@* 相同(即:获取任何类型的 GVariant)。?@? 相同(即:获取任何基本类型的 GVariant)。rr 相同(即:获取任何元组类型的 GVariant)。

在类型字符串前面遇到 @ 时,g_variant_get() 接受指向 (GVariant *)(即 (GVariant **))的指针,并将其设置为对包含值的 GVariant 的新引用(而不是按照惯例将值解构成 C 类型)。可以提供 NULL 来忽略值。*?r 以类似于上述内容的方式处理。

你可以始终将 * 用作 ?r 或任何 @ 用法的替代。但是,如果可能的话,建议使用其他字符,因为这可以提高类型安全性和代码自注释性。

示例

GVariant *value1, *value2;

value1 = g_variant_new ("(i@ii)", 44, g_variant_new_int32 (55), 66);

/* note: consumes floating reference count on 'value1' */
value2 = g_variant_new ("(@(iii)*)", value1, g_variant_new_string ("foo"));

{
  const gchar *string;
  GVariant *tmp;
  gsize length;
  gint x, y, z;

  g_variant_get (value2, "((iii)*)", &x, &y, &z, &tmp);
  string = g_variant_get_string (tmp, &length);
  g_print ("it is %d %d %d %s (length=%d)\n", x, y, z, string, (int) length);
  g_variant_unref (tmp);

  /* quick way to skip all the values in a tuple */
  g_variant_get (value2, "(rs)", NULL, &string); /* or "(@(iii)s)" */
  g_print ("i only got the string: %s\n", string);
  g_free (string);
}

字典

字符:{}

通过首先处理键,然后处理值来处理字典条目。每个值都按照惯例处理。

示例

GVariantBuilder *b;
GVariant *dict;

b = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (b, "{sv}", "name", g_variant_new_string ("foo"));
g_variant_builder_add (b, "{sv}", "timeout", g_variant_new_int32 (10));
dict = g_variant_builder_end (b);

可以通过变量来提取嵌套字典中的数据。

示例

GVariant *data;
gint value = 1;
gint max = 3;

/* type (oa{sa{sv}) */
data = g_variant_new_parsed ("(%o, {'brightness': {'value': <%i>, 'max': <%i>}})",
                             "/object/path", value, max);
{
  GVariant *params;
  GVariant *p_brightness;
  gchar *obj
  gint p_max;

  g_variant_get (data, "(o@a{?*})", &obj, &params);
  g_print ("object_path: %s\n", obj);

  p_brightness = g_variant_lookup_value (params, "brightness", G_VARIANT_TYPE_VARDICT);
  g_variant_lookup (p_brightness, "max", "i", &p_max);
  g_print ("max: %d\n", p_max);
}

指针

字符:&

& 字符用于指示已序列化的数据应通过指针直接交换。

目前,此字符的唯一用法是将其应用于字符串(即:&s&o&g)。对于 g_variant_new(),这完全不起作用。字符串通常按收集和复制。对于 g_variant_get(),表示它不会创建一个新分配的字符串副本,而是返回已序列化的数据的指针。不应释放此指针。将执行有效性检查,以确保字符串数据始终正确以 null 结尾。

示例

{
  const gchar *str;
  GVariant *value;

  value = g_variant_new ("&s", "hello world");
  g_variant_get (value, "&s", &str);
  g_print ("string is: %s\n", str);
  /* no need to free str */
}

便利转换

字符:^

^ 字符目前支持与字节字符串转换,或与字符串或字节字符串数组转换。不支持字节数组。它有多种形式。

使用 g_variant_new() 时,所有形式从可变参数中收集一个指针值并将其传递给一个函数(如下表所示)。该函数的结果用作此位置的值。使用 g_variant_get() 时,一个指针值通过使用该函数(在表中给出)产生并通过引用返回。

转换 g_variant_new() 搭配使用 g_variant_get() 搭配使用
^as 相当于 g_variant_new_strv() 相当于 g_variant_dup_strv()
^a&s 相当于 g_variant_get_strv()
^ao 相当于 g_variant_new_objv() 相当于 g_variant_dup_objv()
^a&o 相当于 g_variant_get_objv()
^ay 相当于 g_variant_new_bytestring() 等同于 g_variant_dup_bytestring()
^&ay 等同于 g_variant_get_bytestring()
^aay 等同于 g_variant_new_bytestring_array() 等同于 g_variant_dup_bytestring_array()
^a&ay 等同于 g_variant_get_bytestring_array()