gpt4 book ai didi

c - 为什么即使回调参数与 XML 中的参数不匹配,GObject 方法仍会被调用?

转载 作者:可可西里 更新时间:2023-11-01 11:46:15 29 4
gpt4 key购买 nike

假设我有这样的方法

<interface name="org.Test.ChildTest">
<!-- set_age(guint32 new_age): sets new age -->
<method name="set_age">
<arg type="u" name="new_age" direction="in"/>
</method>

在我的方法表中,我有:
{ (GCallback) child_test_set_age, dbus_glib_marshal_child_test_BOOLEAN__UINT_POINTER, 0 }

正确的 GObject 方法签名是:
gboolean
child_test_set_age (ChildTest *childTest, guint ageIn, GError** error)

为什么我的方法, child_test_set_age() ,即使回调参数与我的 XML 中指定的参数不匹配,仍然会在 DBus 上被调用?例如,如果我在 guint ageIn 之后添加另一个参数,如 char*guint或其他一些随机类型?

我注意到如果 DBus 函数包含方向为 OUT 的成员,这将不起作用。似乎 IN 类型的任何不必要的参数都会被丢弃,并且调用照常进行。

虽然我相信这没有任何区别,但我使用的是 D-BUS Binding Tool 0.94、glib-2.30.0 和 dbus-glib 0.94。

最佳答案

您发现了由于 C 语言而存在的一个有趣的细节。 C 中的函数是强类型的,所以严格来说,你必须有函数来处理每一种可能的回调类型,就像下面的噩梦一样:

g_signal_connect_callback_void__void(GObject *object, gchar *signal,
void (*callback)(GObject *, gpointer), gpointer data);
g_signal_connect_callback_void__guint(GObject *object, gchar *signal,
void (*callback)(GObject *, guint, gpointer), gpointer data);
g_signal_connect_callback_gboolean__gdkevent(GObject *object, gchar *signal,
gboolean (*callback)(GObject *, GdkEvent *, gpointer), gpointer data);

幸运的是,C 语言的两个特性可以避免这种困惑。
  • 无论函数的返回类型和参数如何,函数指针都保证大小相同。
  • C calling convention (技术上是依赖于编译器和依赖于体系结构的实现细节!)

  • 由于函数指针的大小都相同,因此可以将它们全部转换为 void (*callback)(void) ,这就是 GCallback是一个类型定义。 GCallback在所有 GLib 平台 API 中用于可以具有可变数量和类型的参数的回调。这就是为什么你必须投 child_test_set_ageGCallback在上面的代码示例中。

    但是即使你可以传递函数指针,就好像它们都是一样的,你如何确保函数真正得到它们的参数?这就是 C 调用约定的用途。编译器生成代码,使得调用者将函数的参数插入堆栈,函数从堆栈中读取参数但不弹出它们,当它返回时,调用者将参数从堆栈中弹出。所以调用者可以推送与函数预期不同数量的参数,只要函数可以找到它尝试访问的所有参数!

    让我们用你的例子来说明:调用方法 child_test_set_age(ChildTest *childTest, guint ageIn, GError **error) .假设您的平台上的指针和整数大小相同,我将跳过一些细节以了解总体思路。

    调用者将参数放入堆栈:
    +------------+
    | &childTest | arg1
    +------------+
    | 25 | arg2
    +------------+
    | NULL | arg3
    +------------+

    ...并调用该函数。该函数获取该堆栈并在那里查找其参数:
    +------------+
    | &childTest | ChildTest *childTest
    +------------+
    | 25 | guint ageIn
    +------------+
    | NULL | GError **error
    +------------+

    一切都好。然后函数返回,调用者将参数从堆栈中弹出。

    然而,现在,如果你给你的函数一个不同于 XML 中 DBus 签名的类型,比如说 child_test_set_age(ChildTest *childTest, guint ageIn, guint otherNumberIn, GError **error) ,假设相同的参数被压入堆栈,但您的函数对它们的解释不同:
    +------------+
    | &childTest | ChildTest *childTest ...OK so far
    +------------+
    | 25 | guint ageIn ...still OK
    +------------+
    | NULL | guint otherNumberIn ...will be 0 if you try to read it, but OK
    +------------+
    | undefined | GError **error ...will be garbage!
    | behavior |
    | land!! |
    | ... |

    前两个参数没问题。第三个,因为 DBus 不知道你期待另一个 guint , 将是 GError **转换为 guint .如果你足够幸运,那个指针是 NULL ,然后 otherNumberIn将等于 0,否则它将是一个转换为整数的内存位置:垃圾。

    第四个参数特别危险,因为它会尝试从堆栈中读取一些内容,而您不知道那里有什么。因此,如果您尝试访问 error指针,您可能会因段错误而使程序崩溃。

    但是,如果你设法在没有段错误的情况下通过函数,那么调用者将在函数返回后巧妙地从堆栈中弹出三个参数,一切都会恢复正常。这就是为什么您的程序似乎仍然有效的原因。

    “out”参数是使用指针参数实现的,这就是它们不起作用的原因;该函数几乎可以保证写入无效的内存地址,因为指针是垃圾。

    总之,如果满足以下条件,您的函数可以具有不同的签名:
  • 您的编译器使用 C 调用约定
  • 您的函数的参数数量与调用者期望的相同(或更少)
  • 您的参数与调用者期望推送的参数大小相同
  • 在转换调用者期望推送的参数时,你的参数类型是有意义的
  • 关于c - 为什么即使回调参数与 XML 中的参数不匹配,GObject 方法仍会被调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22402597/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com