GObject学习教程---第九章:函数指针、回调函数与 GObject 闭包

Gstreamer基础之Gobject 文章目录Gobject简介 GStreamer是依附于GLib 2.0对象模型的,采用了信号与对象属性的机制,所有的GStreamer对象都采用GObject继承的方法进行扩展,是所有GStreamer对象的基类,因此有必要学习一下Gobject是怎么回事。 Gobject简介 ... 阅读详情

索引:https://blog.youkuaiyun.com/knowledgebao/article/details/84633798

本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:

楼主见解:

主要讲解接口函数指针、回调函数与 GObject 闭包。目前没看到有啥用,后续再补充见解。


源博客网址:http://garfileo.is-programmer.com/categories/6934/posts

 

函数指针、回调函数与 GObject 闭包

本文首先复习一下基于 C 语言函数指针和回调函数的概念,进而学习 GObject 闭包的用法。这些知识都与面向对象程序设计基本上没有什么关系。

函数指针

所谓函数指针,就是可以指向函数的指针,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

int

foo (void)

{

        return 1;

}

 

int

main (void)

{       

        int (*func) (void);

 

        func = foo;

        func ();

 

        return 0;

}

代码中的 func 即为一个函数指针,它可以指向一个无参数且返回值为整型数的函数,还可以调用它。

只要不会将:

1

int (*func) (void);

与

1

int *func (void);

弄混(后者是返回值类型为整型数指针的函数),那么对于函数指针的理解就没什么大问题了。

由于 "int (*func) (void)" 这种声明函数指针的形式看上去有点怪异,所以很多人喜欢用 typedef 将函数值指针定义成类型以便使用,例如:

1

2

3

typedef int (*Func) (void);

 

Func func = foo;

如果对于上述内容理解起来并不费劲,那么下面就可以谈谈回调函数了。

回调函数

在编写一些程序库的时候,设计者有时会认为有些功能,不应该由他自作主张,而应当由使用者来决定。这方面,比较有名的例子是 C 标准库中的 qsort 函数:

1

2

3

4

void qsort (void *base,

            size_t nmemb,

            size_t size,

            int (*compar) (const void *, const void *));

它的第 4 个参数即为函数指针,它可以指向形式为:

1

int foo (const void *param1, const void *param2);

的所有函数。

Q:这个函数指针的作用是什么?

A:它是用来指向用户提供的回调函数的。

Q:用户提供的回调函数是什么意思?

A:因为 qsort 的设计目标是对任意类型的同类数据集合进行快速排序,比如一组整型数的排序,或者一组字符串的排序。既然是排序,那么 qsort 必须知道如何判定具体类型的两个数据的大小,而 qsort 的设计者认为这一功能应当交给用户来实现,因为他并不清楚用户究竟要使用 qsort 对哪种类型的数据集合进行排序,只有用户自己清楚。

Q:用户提供的回调函数究竟是什么意思?

A:就是对于用户所要排序的数据集合而言,用户提供一个可以比较该数据集合中任意两个元素大小的函数,这个函数便是 qsort 的回调函数。

用户、被调用的函数 qsort 以及回调函数,它们之间的关系如下图所示:

下面是使用 qsort 函数进行字符串数组递增排序的示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

static int

str_compare (const void *s1, const void *s2)

{

        char *str1 = *(char **)s1;

        char *str2 = *(char **)s2;      

        size_t l1 = strlen (str1);

        size_t l2 = strlen (str2);

 

        if (l1 > l2)

                return 1;

        else if (l1 == l2)

                return 0;

        else

                return -1;

}

 

int

main (void)

{

        char *str_array[5] = {"a", "abcd", "abc", "ab", "abcde"};

        qsort (str_array, 5, sizeof (char *), str_compare);

 

        for (int i = 0; i< 5; i++)

                printf ("%s ", str_array[i]);

        printf ("\n");

         

        return 0;

}

闭包(Closure)的概念

从上一节,我们通过函数指针向 qsort 函数传入了一个函数 str_compare,这个函数被称为回调函数,但是它还有一个比较深奥的名字——“闭包”。

所谓闭包,简而言之,就是一个函数加上它所访问的所有非局部变量,而所谓“非局部变量”,表示这些变量对于那个函数而言既非局部变量,也非全局变量。

我们向 qsort 传入函数 str_compare,它所接受排序数据集合中的 2 个元素,而且 2 个元素对于 str_compare 而言,既非是全局变量,也非其局部变量,因此 str_compare 与这 2 个参数形成了一个闭包。

在许多动态语言中,闭包通常也被昵称为“函数是第一类对象”,即函数与那些语言中基本类型具有相同的权利,例如函数可以存储在变量中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。

恶魔来临

在 C 语言中,利用函数指针并配合参数的复制与传递,可模拟闭包这种结构,但是在可读性上没有那些内建支持闭包的语言优雅。

GObject 提供了 GClosure 对象与方法,实现了功能比较全面的 C 闭包模拟,我们可以在程序中直接使用它。下面,通过一个很小的示例,演示 GClosure 的使用。

先来看一个非 GClosure 的 C 闭包示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

#include <stdio.h>

#include <math.h>

#include <string.h>

 

typedef int (*Func) (void *, void *);

 

static void

compare (void *a, void *b, Func callback)

{

        int r = callback (a, b);

         

        if (r == -1)

                printf ("a < b\n");

        else if (r == 0)

                printf ("a = b\n");

        else

                printf ("a > b\n");

}

 

static int

float_compare (void *a, void *b)

{

        float *f1 = (float *)a;

        float *f2 = (float *)b;

 

        if (*f1 > *f2)

                return 1;

        else if (fabs (*f1 - *f2) <= 10E-6)

                return 0;

        else

                return -1;

}

 

static int

str_compare (void *a, void *b)

{

        size_t len1 = strlen ((char *)a);

        size_t len2 = strlen ((char *)b);

 

        if (len1 > len2)

                return 1;

        else if (len1 == len2)

                return 0;

        else

                return -1;

}

 

int

main (void)

{      

        float a = 123.567;

        float b = 222.222;

        Func func = float_compare;

        compare (&a, &b, func);

         

        char *s1 = "hello world!";

        char *s2 = "hello!";

        func = str_compare;

        compare (s1, s2, func);

         

        return 0;

}

上述代码主要实现了一个 compare 函数,它可以比较两个任意类型数据的大小,前提是你要向它提供特定的回调函数(闭包),例如代码中的 float_compare 与 str_compare 函数,它们分别实现了浮点数比较与字符串比较。

将上述程序改为 GClosure 实现,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

#include <math.h>

#include <glib-object.h>

 

void

g_cclosure_user_marshal_INT__VOID_VOID (GClosure     *closure,

                                        GValue       *return_value G_GNUC_UNUSED,

                                        guint         n_param_values,

                                        const GValue *param_values,

                                        gpointer      invocation_hint G_GNUC_UNUSED,

                                        gpointer      marshal_data)

{

        typedef gint (*GMarshalFunc_INT__VOID_VOID) (gpointer     data1,

                                                     gpointer     data2);

        register GMarshalFunc_INT__VOID_VOID callback;

        register GCClosure *cc = (GCClosure*) closure;

        register gpointer data1, data2;

        gint v_return;

         

        g_return_if_fail (return_value != NULL);

        g_return_if_fail (n_param_values == 1);

         

        if (G_CCLOSURE_SWAP_DATA (closure))

        {

                data1 = closure->data;

                data2 = g_value_peek_pointer (param_values + 0);

        }

        else

        {

                data1 = g_value_peek_pointer (param_values + 0);

                data2 = closure->data;

        }

        callback = (GMarshalFunc_INT__VOID_VOID) (

                marshal_data ? marshal_data : cc->callback);

         

        v_return = callback (data1, data2);

         

        g_value_set_int (return_value, v_return);

}

 

static void

compare (GClosure *closure, void *b)

{

        GValue return_value = {0};

        GValue param_value  = {0};

        g_value_init (&return_value, G_TYPE_INT);

        g_value_init (&param_value, G_TYPE_POINTER);

         

        g_value_set_pointer (&param_value, b);

 

        g_closure_invoke (closure, &return_value, 1, &param_value, NULL);

        gint r = g_value_get_int (&return_value);

         

        if (r == -1)

                g_print ("a < b\n");

        else if (r == 0)

                g_print ("a = b\n");

        else

                g_print ("a > b\n");

 

        g_value_unset (&return_value);

        g_value_unset (&param_value);

}

 

static gint

float_compare (void *a, void *b)

{

        gfloat *f1 = a;

        gfloat *f2 = b;

         

        if (*f1 > *f2)

                return 1;

        else if (fabs (*f1 - *f2) <= 10E-6)

                return 0;

        else

                return -1;

}

 

static gint

str_compare (void *a, void *b)

{

        size_t len1 = g_utf8_strlen ((gchar *)a, -1);

        size_t len2 = g_utf8_strlen ((gchar *)b, -1);

 

        if (len1 > len2)

                return 1;

        else if (len1 == len2)

                return 0;

        else

                return -1;

}

 

int

main (void)

{

        g_type_init ();

         

        gfloat a = 123.567;

        gfloat b = 222.222;

        GClosure *closure =

                g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);

        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, &b);

        g_closure_unref (closure);

         

        gchar *s1 = "Hello World!\n";

        gchar *s2 = "Hello!\n";

        closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);

        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, s2);

        g_closure_unref (closure);

         

        return 0;

}

很恐怖,看上去 GClosure 并没有简化闭包的实现,反而将其更加复杂化了,比如多出了 GClosure、GValue 等数据类型,还多出来一个 my_cclosure_user_marshal_INT__VOID_VOID 函数,而且 compare 函数的第 1 个参数的类型也变成 GClosure 指针类型了。

理解 GClosure

对于上一节所给出的 GClosure 的应用示例,我们从 main 函数入手解读一下。

首先看下面的代码:

1

2

3

4

5

6

7

8

/* main 函数中的代码 */

        gfloat a = 123.567;

        gfloat b = 222.222;

        GClosure *closure =

                g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);

        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, &b);

        g_closure_unref (closure);

g_cclosure_new 函数创建了一个面向 C 语言的闭包。之所以在此强调是面向 C 语言,那是因为 GObject 的闭包是面向任意语言的,例如 pyg_closure_new 函数可以创建 python 语言的闭包。

g_cclosure_new 函数所创建的 C 语言闭包结构体是 GCClosure,其定义如下:

1

2

3

4

5

typedef _GCClosure GCClosure;

struct _GCClosure {

        GClosure    closure;

        gpointer    callback;

};

联想一下 GObject 的子类化,上述代码暗示着 GCClosure 是 GClosure 的子类,不过这里没有类结构体以及实例化等概念,仅仅是结构体的继承。

在阅读下面的内容之前,一定要注意分辨 GCClosure 与 GClosure,它们的名字太相似了。

GCClosure 结构体包含了一个 GClosure 结构体 closure 以及一个无类型的数据指针 callback(因为 gpointer 是 void * 的别名)。前者是 GObject 提供的闭包结构,后者是一个指向回调函数的指针。这里一定要注意,用无类型指针指向一个函数,是 GObject 设计的一个缺陷,因为它假定了数据类型的指针与函数指针的存储宽度是相等的,虽然尽管目前大部分 PC 的 CPU 支持这一假设,但是在 C99 标准里,使用无类型的数据指针指向函数,这属于未定义的行为。

GClosure 结构体主要是面向各种语言对闭包的公有功能进行了基本抽象,这样所有要与 GObject 闭包机制打交道的语言,可以先继承 GClosure 结构体,然后再根据自身需要再添加一些特定的数据成员。例如,GCClosure 添加了 callback 无类型数据指针,用于指向闭包所对应的回调函数。

GClosure 结构体中包含许多成员,其中对于使用者较为重要的三个成员是 marshal、data 与 marshal_data,其中 marshal 是一个函数指针,指向一个回调函数的调用者;data 与 marshal_data 皆为 gpointer 指针类型。对于 C 语言闭包,data 指向用户向回调函数传递的数据地址,而 marshal_data 则指向回调函数。我知道,这句话也许会引起下面的问答。

Q:那个 GCClosure 结构体中不是已经有了一个指向回调函数的 callback 指针吗?

A:这是因为callback 所指向的函数是闭包默认的回调函数,而 GObject 库允许你自由切换回调函数,可将 marshal_data 看作是一个开关,它可以暂时屏蔽 GCClosure 中的 callback 所指向的回调函数,而启用 marshal_data 所指向的回调函数。事后,可以调用 g_closure_set_meta_marshal 函数将 marshal_data 所指向的回调函数屏蔽,这样闭包的回调函数又被切换为 callback 所指向的回调函数了。下文会给出一个具体的示例来验证这一说法。

从 GObject 库使用者的角度,我们对 GClosure 结构体作用的理解在此止步即可。如果像文档 [1] 那样的深入挖掘,那么就好比是一个普通人在不具备医生心理素质的情况下解剖任何一种动物,恶心是难免的。

我们将话题转回刚才所讨论的 main 函数中的代码片段。看下面的代码:

1

2

GClosure *closure = g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);

g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

它们一起实现了下图所示的数据结构:

紧接着,将所建立的闭包 closure 作为第一个参数,浮点数变量 b作为第 2 个参数,代入函数 compare,即:

1

compare (closure, &b);

因为 closure 中即包含了浮点数变量 a,也包含了浮点数比较函数 float_compare,因此在 compare 函数中,使用 float_compare 函数可进行浮点数 a 与 b 大小比较,是显而易见的。但事实上,在 compare 函数中,我们并没有直接调用 float_compare 函数,而是将这个任务交给了 g_closure_invoke 函数。

来看 compare 函数的主干部分:

1

2

3

4

5

6

7

8

9

10

static void

compare (GClosure *a, void *b)

{

        ... ...

 

        g_closure_invoke (a, &return_value, 1, &param_value, NULL);

        gint r = g_value_get_int (&return_value);

 

        ... ...

}

由于 compare 函数的第一个参数是闭包 closure,它已经包含了浮点数变量 a 和回调函数 float_compare。当我们将 closure 作为 g_closure_invoke 函数的参数时,后者会基于 closure 中的 marshal 指针调用 g_closure_user_marshal_INT__VOID_VOID 函数,而 g_closure_user_marshal_INT__VOID_VOID 函数是我们自己定义的,对于本文示例而言其主干部分如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

void

g_cclosure_user_marshal_INT__VOID_VOID ( ... )

{

        ... ... ...

 

        if (G_CCLOSURE_SWAP_DATA (closure))

        {

                data1 = closure->data;

                data2 = g_value_peek_pointer (param_values + 0);

        }

 

        ... ... ...

 

        callback = (GMarshalFunc_INT__VOID_VOID) (

                marshal_data ? marshal_data : cc->callback);

         

        v_return = callback (data1, data2);

 

        ... ... ...

}

上述代码所做的工作无非是从 closure 闭包中先获取浮点数变量 a,即 data1,然后从 g_cclosure_user_marshal_INT__VOID_VOID 函数的参数中获取浮点数变量 b,即 data2,然后再从 closure 闭包中获得 callback 指针所指向的回调函数,即 float_compare,最终完成浮点数 a 与 b 的大小比较。

闭包调用的整个过程大致如下图所示:

GClosure 闭包的工作过程非常繁琐,它所做的主要工作就是向回调函数的调用过程中插入两个环节,即 g_closure_invoke 函数与 g_closure_user_marshal_*__** 函数。这样做的主要目的是提高闭包的灵活性。就像是链条,如果只有 2 个节点,那么它只能像一根直线段那样生存,如果将其改造成 4 个节点,它就变成了一条 3 次曲线了!

更换 marshal

前文说过,GClosure 结构体中有一个 marshal_data 指针,它也可以指向一个回调函数,并且会取代 GCClosure 结构体中 callback 指针所指向的回调函数。为了充分说明这一点,我们对前文的 GClosure 示例进行一些修改,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

#include <math.h>

#include <glib-object.h>

 

void

g_cclosure_user_marshal_INT__VOID_VOID (GClosure     *closure,

                                        GValue       *return_value G_GNUC_UNUSED,

                                        guint         n_param_values,

                                        const GValue *param_values,

                                        gpointer      invocation_hint G_GNUC_UNUSED,

                                        gpointer      marshal_data)

{

        typedef gint (*GMarshalFunc_INT__VOID_VOID) (gpointer     data1,

                                                     gpointer     data2);

        register GMarshalFunc_INT__VOID_VOID callback;

        register GCClosure *cc = (GCClosure*) closure;

        register gpointer data1, data2;

        gint v_return;

         

        g_return_if_fail (return_value != NULL);

        g_return_if_fail (n_param_values == 1);

         

        if (G_CCLOSURE_SWAP_DATA (closure))

        {

                data1 = closure->data;

                data2 = g_value_peek_pointer (param_values + 0);

        }

        else

        {

                data1 = g_value_peek_pointer (param_values + 0);

                data2 = closure->data;

        }

        callback = (GMarshalFunc_INT__VOID_VOID) (

                marshal_data ? marshal_data : cc->callback);

         

        v_return = callback (data1, data2);

         

        g_value_set_int (return_value, v_return);

}

 

static void

compare (GClosure *closure, void *b)

{

        GValue return_value = {0};

        GValue param_value  = {0};

        g_value_init (&return_value, G_TYPE_INT);

        g_value_init (&param_value, G_TYPE_POINTER);

         

        g_value_set_pointer (&param_value, b);

 

        g_closure_invoke (closure, &return_value, 1, &param_value, NULL);

        gint r = g_value_get_int (&return_value);

         

        if (r == -1)

                g_print ("a < b\n");

        else if (r == 0)

                g_print ("a = b\n");

        else

                g_print ("a > b\n");

 

        g_value_unset (&return_value);

        g_value_unset (&param_value);

}

 

 

static gint

str_compare (void *a, void *b)

{

        size_t len1 = g_utf8_strlen ((gchar *)a, -1);

        size_t len2 = g_utf8_strlen ((gchar *)b, -1);

 

        if (len1 > len2)

                return 1;

        else if (len1 == len2)

                return 0;

        else

                return -1;

}

 

static gint

str_compare_new (void *a, void *b)

{

        g_print ("\nI'm a new marshaller\n");

 

        return (str_compare (a, b));

}

 

int

main (void)

{

        g_type_init ();

         

        gchar *s1 = "Hello World!\n";

        gchar *s2 = "Hello!\n";

 

        GClosure *closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);

        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, s2);

 

        g_closure_set_meta_marshal (closure, str_compare_new,

                                    g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, s2); 

 

        g_closure_unref (closure);

         

        return 0;

}

上述代码所做的修改,主要是删除了有关浮点数比较的示例部分,然后添加了一个新函数 str_compare_new,并对 main 函数中字符串比较部分的代码做了变动,重点观察以下代码:

1

2

3

4

5

6

7

8

/* main 函数代码片段 */

        GClosure *closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);

        g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, s2);

 

        g_closure_set_meta_marshal (closure, str_compare_new,

                                    g_cclosure_user_marshal_INT__VOID_VOID);

        compare (closure, s2); 

第一次调用 compare 函数,闭包函数调用链的终端是 str_compare 函数,而第二次调用 compare 函数时,闭包函数调用链的终端是 str_compare_new 函数,它再去调用 str_compare 实现字串比较。这是因为,在第二次调用 compare 函数之前,我们通过 g_closure_set_meta_marshal 函数设置了 closure 闭包的 marshal_data 指针,使其指向了 str_compare_new 函数。这样,在 g_cclosure_user_marshal_INT__VOID_VOID 函数中,代码:

1

callback = (GMarshalFunc_INT__VOID_VOID) (marshal_data ? marshal_data : cc->callback);

callback 的值会是 marshal_data,而非 cc->callback。

如何又快又好的产生 g_cclosure_user_marshal_* 函数?

GLib 库提供了一个名为 glib-genmarshal 的工具,它可以根据我们给出的函数描述信息产生有效的 marshal 代码。上文中的 g_cclosure_user_marshal_INT__VOID_VOID 函数,我便是使用这个工具产生的。

首先,准备好一份文本文档,例如 in__void_void.txt:

1

INT:VOID,VOID

然后,执行命令:

1

$ glib-genmarshal --body int__void_void.txt > int__void_void.c

即可产生 g_cclosure_user_marshal_INT__VOID_VOID 函数。

另外,GObject 也预定义了一组 marshal 函数,可参考文档 [2] 中所有 g_cclosure_marshal_ 为前缀的函数。

参考文档

[1] 恶心的GObject[Part I][v0.1]

[2] GObject 手册的 GClosure 部分
 

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

合宙Air780EP速连OneNET:AT+MQTT应用指南 本文详细讲解了合宙Air780EP使用AT指令通过MQTT接入OneNET的过程。 阅读详情

相关推荐

细说STM32单片机SPI-Flash芯片的FatFS移植及其应用实例详解

本文以开发板上的SPI-Flash芯片W25Q16为媒介详细介绍CubeMX中FatFS各个参数的意义和设置,分析生成的代码中FatFS各个文件的作用和关联,完成针对W25Q16的硬件访问层的移植。继续使用旺宝红龙开发板STM32F407ZGT6 KITV1.0。

wenchm的博客 1640

6. Gobject 学习总结

简单的来说,GObject是一个程序库,它可以帮助我们使用C语言编写面向对象的程序。   很多人被灌输了这样一种概念:要写面向对象程序,那么就需要学习一种面向对象编程语言,例如C++、Java、C# 等等,而 C 语言是用来编写结构化程序的。事实上,面向对象只是一种编程思想,不是一种编程语言。换句话说,面向对象是一种游戏规则,它不是游戏。GObject 告诉我们,使用 C 语言编写程序时,可以

吾皇的博客 4778

避坑指南:Cursor AI生成Flutter代码时最容易翻车的5个配置细节

本文深入剖析了使用Cursor AI生成Flutter代码时,因.cursorrules配置文件不当而导致的五大常见问题。重点探讨了如何通过制定清晰、具体的规则,避免状态管理混乱、设计图输入低效、项目架构脱节、性能隐患及规则维护缺失等陷阱,从而将AI转化为高效、可靠的开发伙伴,提升代码质量和团队协作效率。

wdx0123456的博客 415

GObject官方详细文档

目录 背景 数据类型和编程 导出C API GLib动态类型系统 复制功能 约定 不可实例化的基本类型 (如gchar) 可实例化的类型:对象 初始化和销毁 不可实例化的类型:接口 接口初始化 接口销毁 GObject基类 对象实例化 对象内存管理 引用计数 弱引用 引用计数和周期 对象属性 一次访问多个属性 GObject消息传递系统 关闭 C闭包 ...

Ysu_LiuYang的博客 7098

Zynq工程IP核封装:XCI引用与约束继承实战指南

IP核封装是FPGA系统设计中实现模块复用、团队协作与技术资产沉淀的关键环节。其本质是将功能逻辑、时序约束、接口协议和硬件配置统一抽象为可配置、可验证、可演进的标准化组件。核心原理在于通过XCI文件保留IP核的参数化定义能力,依托Vivado IP Packager实现约束继承、端口自动映射与时钟域解耦,从而保障跨项目集成的时序可信性与约束完备性。该技术广泛应用于Zynq SoC视频处理、高速接口(如HDMI/TMDS)、多时钟域控制系统等场景,尤其在需要长期维护与版本迭代的工业级嵌入式开发中具有不可替代的

weixin_42593701的博客 122

数据工程师——ETL

文章摘要 ETL(数据抽取、转换、加载)是数据处理的核心流程,负责将分散数据源整合为高质量数据。ETL与ELT的主要区别在于转换步骤顺序,分别适用于不同规模数据场景。数据仓库通过ETL从多个数据源集成数据,支持分析决策。维度建模中的事实表记录业务指标,维度表提供上下文信息。缓慢变化维度(SCD)处理随时间变化的维度数据,常见类型包括直接覆盖型、新增版本型等。数据清洗解决数据缺失、重复、错误等问题,确保数据质量。数据血缘记录数据流转路径,便于问题排查和影响分析。常用ETL工具如Kettle通过转换和作业实现数

weixin_45729710的博客 1559

GObject 简明教程(一)

最近,我一直在深入学习 GStreamer(相关教程可以从我主页中看到)。在探索其复杂的设计逻辑时,我意识到需要深入研究 GStreamer 的源码。而因为 GStreamer 是基于 GObject 开发的,掌握 GObject 的基本概念就显得尤为重要。本文整理自,简化了细节,保留了一些关键内容,以便更好地理解。提示:以下是本篇文章正文内容,下面案例可供参考。

记录学习的过程 2297

基于51单片机的多功能计算器全套设计

当今社会,随着人们物质生活的不断提高,电子产品已经走进了家家户户,无论是生活或学习,还是娱乐和消遣几乎样样都离不开电子产品,大型复杂的计算能力是人脑所不能胜任的,而且人脑比较容易出错。计算器作为一种快速通用的计算工具方便了用户的使用。计算器可谓是我们最亲密的电子伙伴之一。本设计着重在于分析计算器设计开发过程中的环节和步骤,并从实践经验出发对计算器设计做了详细的分析和研究。 本设计是以STC89C52单片机为核心的计算器模拟系统设计,输入采用5×8矩阵键盘,可以进行加、减、乘、除等十几种数字运算,同时支持括号

runweipa的博客 1742

GObject对象系统

GObject对象系统 简单的说,GObject对象系统是一个建立在GLIB基础上的, 用C语言完成的,具有跨平台特色的、灵活的、可扩展的、非常容易映射到其它语言的面向对象的框架。 如果你是一个C语言的执着的追随者,你没有理由不研究一下它。 NOTE: glib库是Linux平台下最常用的C语言函数库,它具有很好的可移植性和实用性。 glib是Gtk +库和Gnome的基础。 g

北雨南萍 2126

群无人机航迹规划-虚拟管道

图源北理工全权老师团队虚拟管道相关论文,文字为个人理解,以ppt形式分享报告。

coldmood77的博客 669

大模型——CoPaw:阿里云通义推出的个人智能体工作台,让 AI 助理真正落地

阿里云通义实验室于2026年2月发布CoPaw个人智能助理,采用"本地+云端"双部署模式,支持多平台接入和技能扩展。该产品基于AgentScope多智能体生态构建,具备极简部署、长期记忆系统和灵活的技能插件体系三大核心特性。用户可通过简单命令快速安装,并通过记忆文件系统实现个性化交互体验。CoPaw还支持钉钉、飞书等主流平台接入,并开源了模块化架构,允许本地模型接入。技术架构采用多频道对话网关与可插拔Skills系统,为用户提供真正的AI助理服务体验。

2132

Gobject专栏索引

索引首页:https://blog.youkuaiyun.com/knowledgebao/article/details/84776046 官网API:https://developer.gnome.org/gobject/stable/ gobject相关概念:https://blog.youkuaiyun.com/knowledgebao/article/details/82498187 第一章:GObjec...

knowledgebao的博客 7528

zynq的vitis工程烧录到Winbond(华邦)型号W25Q256JVEQ的QSPI FLAHS

vitis2023.1直接支持W25Q256JVEQ。

FPGA艺术家的博客 676

linux - gobject使用

gobject简介sample*.h*.c 简介 Gobject 系统提供了一个灵活的、可扩展的、并且容易映射到其他语言的面向对象的 C 语言框架. (1)是Glib库的动态类型系统实现,它实现了:   [1] 基于引用计数的内存管理   [2] 实例的构造和析构   [3] 通用的set/get的属性获取方法   [4] 简单易用的信号机制 GObject 的动态类型系统允许程序在运行时进行类型注册,主要目的有两个: (1)使用面向对象的设计方法来编程,GObject 仅依赖于 GLib 和 li

qq_25439881的博客 1631

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

GObject入门(一)类和实例

GObject入门

paw5zx的博客 1149

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

快速上手Gobject

What is G-object? —很多人被灌输了这样一种概念:要写面向对象程序,那么就需要学习一种面向对象编程语言,例如C++、Java、C#等等,而C语言是用来编写结构化程序的。 —事实上,面向对象只是一种编程思想,不是一种编程语言。换句话说,面向对象是一种游戏规则,它不是游戏。 —Gobject,亦称Glib对象系统,是一个程序库,它可以帮助我们使用C语言编写面向对象程序;它提供了一

铁匠Smith先生的专栏 2万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

函数指针、回调函数与 GObject 闭包

本文原始出处:http://garfileo.is-programmer.com 本文首先复习一下基于 C 语言函数指针和回调函数的概念,进而学习 GObject 闭包的用法。这些知识都与面向对象程序设计基本上没有什么关系。 函数指针 所谓函数指针,就是可以指向函数的指针,例如: ? 1 2 3 4 5 6 7 8 9 10 11 12

铁匠Smith先生的专栏 2875

GObject学习笔记(一)类和实例

类结构体会被注册到类型系统中(具体注册方式在下一节讨论),在g_object_new首次调用时,类型系统会检查相应的类结构体是否已经被初始化为一个类变量,没有则创建并初始化。此后所有该类的实例变量都将共享这个已初始化的类变量。在GObject系统中,类结构体和实例结构体都会被实例化,在内存中占有特定的空间。为了便于描述,我们将分配给类结构体的实例称为“类变量”,而分配给实例结构体的实例称为“实例变量”。可以发现,两个实例变量的地址不同,但两个实例变量对应的类变量的地址相同,因为两个实例变量共享一个类变量。

weixin_55010563的博客 516

GObject Tutorial

GObject Tutorial Ryan McDougall(2004) 目的 — 这篇文档可用于两个目的:一是作为一篇学习Glib的GObject类型系统的教程,二是用作一篇按步骤的使用GObject类型系统的入门文章。文章从如何用C语言来设计一个面向对象的类型系统开

我们这里还有鱼······ 1505

gstreamer学习笔记---Gobject类对象

因为在g_object_unref()函数中调用dispose()函数和finalize函数是通过宏定义G_OBJECT_GET_CLASS取得OBJECT_CLASS类之后,再调用它的dispose()函数和finalize函数,所以需要在TestObject的类初始化函数对这两个函数指针进行覆盖,而在TestObject类的dispose()函数和finalize函数再通过G_OBJECT_CLASS (parent_class)取得父类指针,调用父类的析构函数。

tianzunmengyan的博客 421

GObject学习笔记(二)类型创建与注册

前言 本文可在https://paw5zx.github.io/GObject-tutorial-beginner-02/中阅读,体验更加 在上一节中我们介绍了GObject类型的类和实例变量的创建和使用。GObject是一个基本的可实例化类类型,是所有使用GObject系统的类型的基类,提供了继承

fenfenfen520a的博客 177

GObject基类

GObject基类 前一章讨论了GLib动态类型系统的细节。GObject库还包含一个名为GObject的基本类型的实现。 GObject是一个基本的类实例化类型。 它实现: (1)内存管理引用计数 (2)实例的构造/析构 (3)具有set/get函数对的通用每个对象属性 (4)轻松使用信号 所有使用GLib类型系统的GNOME库(如GTK +和GStreamer)都继承自GObje

lp525110627的博客 4442

Gstreamer学习笔记(8):Gobject类对象

文章目录1、Gobject类定义2、向Gobject系统注册类3、类的构造函数4、父子类的继承关系5、类的析构函数6、类的其他设置 1、Gobject类定义 Gstreamer框架是基于插件的,同时插件是可以动态的注册、创建,gstreamer基于Gobject开发,下面来了解一下gstreamer是如何通过Gobject完成自定义类的注册。 在每个类的c文件中,都会有以下这样的一个宏定义: /* class initialization */ #define DEBUG_INIT \ GST_DEB

houxiaoni01的博客 1785

sec5-属性

如果定义了多个属性,请使用属性id数组。最好去看Gtk源文件,比如gtklabel.c。GtkLabel有18个属性。在src/tdouble6目录中有一个例子。

Creationyang的博客 1814

[pyvips] GLib | GObject | GValue

我准备了一个盒子`GValue value = G_VALUE_INIT`,放了个整数 42`set_int(&value, 42)`,然后交给别人处理。

No Game No Life 1152
上一篇: GObject学习教程---第八章:GObject 对接口的模拟
下一篇: GObject学习教程---第十章:GObject 的信号机制——概览
knowledgebao
博客等级 码龄15年 401粉丝 198原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值