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 格式字符串中可能出现的符号的粗略含义。每个符号都在其自己的一节中进行了详细描述,包括使用 示例。
符号 | 含义 |
---|---|
b 、y 、n 、q 、i 、u 、x 、t 、h 、d |
用于构建或解构布尔、字节和数字类型。请参见下文的 数字类型。 |
s 、o 、g |
用于构建或解构字符串类型。请参见下文的 字符串。 |
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 类型。请参见下面的 指针。 |
^ |
用作某些特定类型格式字符串的前缀。请参见下面的 便捷转换。 |
数值类型
字符:b
、y
、n
、q
、i
、u
、x
、t
、h
、d
从数值类型进行的可变参数转换以最明显的方式进行。遇到其中一个字符时,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 中,可变参数列表中的小整数类型会根据需要提升到 int
或 unsigned int
,并相应地读回。GLib 当前支持的每个平台上的 int
都是 32 位。这意味着你可以对类型为 int
的 C 表达式与 g_variant_new()
和格式字符 b
、y
、n
、q
、i
、u
和 h
一起使用。具体来说,你可以对这些字符使用整数字面量。
当使用 x
和 t
字符时,你必须确保提供的价值为 64 位。这意味着你应使用强制转换或使用 G_GINT64_CONSTANT
或 G_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);
}
字符串
字符:s
、o
、g
字符串转换在标准空终止的 C 字符串之间进行。在格式字符串中遇到 s
、o
或 g
时,g_variant_new()
会获取 (const gchar *)
并复制一份。NULL
不是有效的字符串;使用可能类型对其进行编码。如果使用 o
或 g
字符,则必须注意确保传递的字符串分别是有效的 D-Bus 对象路径或 D-Bus 类型签名。
在遇到 s
、o
或 g
时,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
后面的字符。
第一种方法适用于从 a
、s
、o
、g
、v
、@
、*
、?
、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
)。r
与 r
相同(即:获取任何元组类型的 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, ¶ms);
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() |