即使回调参数与XML中的参数不匹配,为什么仍会调用GObject方法? [英] Why does GObject method still get called even if callback arguments don't match those in XML?

查看:60
本文介绍了即使回调参数与XML中的参数不匹配,为什么仍会调用GObject方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个这样的方法

Suppose I have a method like this

<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>

在我的方法表中,我有:

In my table of methods I have:

{ (GCallback) child_test_set_age, dbus_glib_marshal_child_test_BOOLEAN__UINT_POINTER, 0 }

正确的GObject方法签名是:

and the right GObject method signature is:

gboolean
child_test_set_age (ChildTest *childTest, guint ageIn, GError** error)

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

Why does my method, child_test_set_age(), still get called on DBus even if the callback arguments don't match the one specified in my XML? For example if I add another argument after guint ageIn, like a char* or guint or some other random type?

我注意到,如果DBus函数包含方向为OUT的成员,则此方法将无效.似乎所有不需要的IN类型参数都将被丢弃,并且调用将照常进行.

I've noticed that this won't work if the DBus function includes members with direction OUT. It seems that any argument of type IN that's unnecessary gets discarded and the call is done as usual.

尽管我相信这没有什么区别,但我使用的是D-BUS绑定工具0.94,glib-2.30.0和dbus-glib 0.94.

Although I believe this does not make any difference, I'm using D-BUS Binding Tool 0.94, glib-2.30.0 and dbus-glib 0.94.

推荐答案

由于C语言,您发现了一个有趣的细节. C语言中的函数是强类型的,因此严格来说,您必须具有处理每种可能的回调类型的函数,例如以下噩梦:

You've hit on an interesting detail that exists because of the C language. Functions in C are strongly typed, and so strictly speaking you'd have to have functions to deal with every single possible type of callback, something like the following nightmare:

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语言的两个功能使得避免这种混乱成为可能.

Luckily two features of the C language make it possible to avoid that mess.

  • 无论函数的返回类型和参数如何,都保证函数指针的大小相同.
  • C调用约定(从技术上讲,依赖于编译器且依赖于体系结构) !)
  • Function pointers are guaranteed to be the same size, no matter the return type and arguments of the function.
  • The C calling convention (technically a compiler-dependent and architecture-dependent implementation detail!)

由于函数指针的大小都相同,因此可以将它们全部转换为void (*callback)(void),这就是GCallback的typedef. GCallback在所有GLib平台API中用于可具有可变数量和参数类型的回调.这就是为什么您必须在上面的代码示例中将child_test_set_age强制转换为GCallback的原因.

Since function pointers are all the same size, it's OK to cast them all to void (*callback)(void), which is what GCallback is a typedef for. GCallback is used in all GLib-platform API for callbacks that can have variable numbers and types of arguments. That's why you have to cast child_test_set_age to GCallback in your code sample above.

但是,即使您可以像传递相同的函数指针一样传递函数指针,您如何确保函数实际上获得其参数呢?这就是C调用约定的目的.编译器生成代码,以便调用者将函数的参数压入堆栈,该函数从堆栈中读取参数,但不弹出它们,返回时,调用者将参数从堆栈中弹出.因此,只要函数可以找到它尝试访问的所有参数,调用方就可以按其预期的数量推送不同数量的参数!

But even if you can pass function pointers around as if they're all the same, how do you make sure the functions actually get their arguments? That's what the C calling convention is for. The compiler generates code such that the caller pushes the function's arguments on to the stack, the function reads the arguments from the stack but doesn't pop them, and when it returns, the caller pops the arguments back off the stack. So the caller can push a different number of arguments than the function expects, as long as the function can find all the arguments it tries to access!

让我们用您的示例进行说明:调用方法child_test_set_age(ChildTest *childTest, guint ageIn, GError **error).假设指针和整数在您的平台上大小相同,而我将跳过一些详细信息以了解大致概念.

Let's illustrate that with your example: calling the method child_test_set_age(ChildTest *childTest, guint ageIn, GError **error). Let's assume pointers and integers are the same size on your platform, and I'll skip over some details in order to get the general idea across.

调用方将参数放入堆栈:

The caller puts the arguments onto the stack:

+------------+
| &childTest |   arg1
+------------+
| 25         |   arg2
+------------+
| NULL       |   arg3
+------------+

...并调用该函数.该函数获取该堆栈并在其中查找其参数:

...and calls the function. The function gets that stack and looks for its arguments there:

+------------+
| &childTest |   ChildTest *childTest
+------------+
| 25         |   guint ageIn
+------------+
| NULL       |   GError **error
+------------+

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

Everything is fine. Then the function returns and the caller pops the arguments off the stack.

但是,现在,如果给函数提供的类型与XML中DBus签名的类型不同,可以说child_test_set_age(ChildTest *childTest, guint ageIn, guint otherNumberIn, GError **error),可以说将相同的参数压入堆栈,但是函数对它们的解释不同:

Now, however, if you give your function a different type than the DBus signature in the XML, let's say child_test_set_age(ChildTest *childTest, guint ageIn, guint otherNumberIn, GError **error), let's say the same arguments get pushed onto the stack, but your function interprets them differently:

+------------+
| &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,否则它将是一个强制转换为整数的内存位置:垃圾.

The first two parameters are fine. The third one, since DBus doesn't know you're expecting another guint, is going to be the GError ** cast to a guint. If you're lucky enough that that pointer was NULL, then otherNumberIn will be equal to 0, but otherwise it'll be a memory location cast to an integer: garbage.

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

The fourth parameter is especially dangerous, because it will try to read something off the stack, and you have no idea what's on there. So if you try to access the error pointer, you'll probably crash your program with a segfault.

但是,如果您设法在没有段错误的情况下顺利通过该函数,则在函数返回后,调用方将巧妙地从堆栈中弹出三个参数,一切将恢复正常.这就是为什么您的程序仍然可以正常工作的原因.

However, if you manage to get through the function without a segfault, then the caller will neatly pop three arguments off the stack after the function returns and everything will be back to normal. So that's why your program still seems to work.

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

The "out" parameters are implemented using pointer arguments, and so that's why they don't work; the function is almost guaranteed to write to an invalid memory address because the pointers are garbage.

总而言之,如果满足以下条件,则您的函数可以具有不同的签名:

In summary, your function can have a different signature if the following conditions hold:

  • 您的编译器使用C调用约定
  • 您的函数具有的参数数量(或更少)与调用者期望的数量相同
  • 您的参数的大小均与调用者希望推送的参数的大小相同
  • 当调用方希望推送的参数时,您的参数类型很有意义

这篇关于即使回调参数与XML中的参数不匹配,为什么仍会调用GObject方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆