GObject Study Notes Summary---9

Source: Internet
Author: User
Tags closure comparison define function printf sort strlen

Transferred from: http://garfileo.is-programmer.com/2011/3/20/function-pointer-and-callback-function-and-closure.25453.html

function pointers, callback functions, and GObject closures

This paper first review the concept of C-language function pointers and callback functions, and then learn the use of GObject closures. This knowledge is largely not related to object-oriented programming. function Pointers

A function pointer is a pointer that can point to a function, for example:

int
foo (void)
{
        return 1;
}
 
int
main (void)
{        
        int (*func) (void);
 
        func = foo;
        Func ();
 
        return 0;
}

The Func in the code is a function pointer that can point to a function that has no parameters and returns an integer number, and can also call it.

As long as it will not:

Int (*func) (void);

And

int *func (void);

To confuse (the latter is a function that returns a value type of integer pointer), then there is no big problem with understanding the function pointer.

Because "Int (*func) (void)" is a somewhat bizarre form of declaring function pointers, many people like to use TypeDef to define function-value pointers as types to be used, for example:

typedef int (*FUNC) (void);
 
func func = foo;

If this is not an effort to understand, then you can talk about the callback function below. callback function

When writing some library, the designer sometimes thinks that some functions should not be made by him, but should be decided by the user. In this respect, the more famous example is the Qsort function in the C standard library:

void Qsort (void *base, 
            size_t nmemb, 
            size_t size,
            int (*compar) (const void *, const void *));

Its 4th parameter is the function pointer, which can be pointed to in the form:
int foo (const void *param1, const void *PARAM2);

All of the functions.

Q: What is the function of this pointer?

A: It is used to point to the callback function provided by the user.

Q: What does the user-provided callback function mean.

A: Because the design goal of Qsort is to quickly sort a collection of similar data of any type, such as the ordering of a set of integers, or the ordering of a set of strings. Since it is a sort, then qsort must know how to determine the size of a specific type of two data, and Qsort's designers believe that this function should be given to the user, because he does not know exactly what type of data collection users want to use Qsort to sort, only the user is clear.

Q: What exactly does the user-provided callback function mean.

A: Just for the data set that the user wants to sort, the user provides a function that can compare the size of any two elements in the data set, which is the callback function of Qsort.

The user, the called function Qsort, and the callback function, the relationship between them is shown in the following figure:

The following is an example of incrementing a string array using the Qsort function:

#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 (Closure) Concept

From the previous section, we passed a function pointer to the Qsort function Str_compare, which is called the callback function, but it also has a more esoteric name-" closure ."

The so-called closure , in short, is a function plus all the non-local variables it accesses , and so-called "non-local variables", which means that these variables are neither local nor global variables for that function.

We pass the function Str_compare to Qsort, which accepts 2 elements in the sorted data set, and 2 elements are neither global nor local to Str_compare, so str_compare forms a closure with these 2 parameters.

In many dynamic languages, closures are often nicknamed "functions are first class objects," where functions have the same rights as basic types in those languages, such as functions that can be stored in variables, passed as arguments to other functions, and as return values for other functions. The Devil is coming

In the C language, the use of function pointers and the copying and passing of parameters can be used to simulate the structure of closures, but there is no readability in which the built-in support closure language is elegant.

GObject provides Gclosure objects and methods to achieve a more comprehensive C-closure simulation, which we can use directly in the program. Below, a very small example demonstrates the use of gclosure.

Let's look at a non-gclosure C closure Example:

#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; }

The code above implements a compare function that compares the size of two data of any type, provided that you provide it with a specific callback function (closure), such as the Float_compare and Str_compare functions in the code. They enable floating-point comparisons and string comparisons, respectively.

Change the above procedure to gclosure implementation, as follows:

#include <math.h> #include <glib-object.h> void g_cclosure_user_marshal_int__void_void (Gclosure *closur
                                        E, Gvalue *return_value g_gnuc_unused,
                                        Guint n_param_values, const gvalue *param_values, Gpointer invocation_hint g_gnuc_unused, Gpointer Mars
                                                     Hal_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 (¶m_value, G_type_pointer);
 
        G_value_set_pointer (¶m_value, b);
        G_closure_invoke (Closure, &return_value, 1,¶m_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 (¶m_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; }

Very scary, it seems gclosure does not simplify the implementation of closures, but it is more complicated, such as the Gclosure, Gvalue and other data types, but also out of a my_cclosure_user_marshal_int__void_void function , and the type of the 1th parameter of the Compare function becomes the gclosure pointer type. Understanding Gclosure

For an example of the Gclosure application given in the previous section, let's start with the main function.

First look at the following code:

/* Code in the main function */
        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);

The G_cclosure_new function creates a closure for the C language. The emphasis here is on the C language, because GObject closures are intended for any language, such as the Pyg_closure_new function to create a closure of the Python language.

The C-language closure structure created by the G_cclosure_new function is gcclosure, which is defined as follows:

typedef _gcclosure Gcclosure;
struct _gcclosure {
        gclosure    closure;
        Gpointer    callback;
};

Reminiscent of GObject, the above code implies that Gcclosure is a subclass of Gclosure, but there is no such concept as class structure and instantiation, just the inheritance of structs.

Before reading the following, be sure to distinguish between Gcclosure and Gclosure, whose names are too similar .

The gcclosure structure contains a gclosure struct closure and an untyped data pointer callback (because Gpointer is an alias of void *). The former is the closure structure provided by GObject, which is a pointer to a callback function. It is important to note that pointing to a function with an untyped pointer is a flaw in the GObject design because it assumes that the data type pointer is equal to the storage width of the function pointer, although most of the current PC CPUs support this assumption, but in the C99 standard, Use the untyped data pointer to point to the function, which belongs to undefined behavior .

Gclosure structure is mainly for various languages to the closure of the public function of the basic abstraction, so that all the GObject closure mechanism to deal with the language, you can first inherit the gclosure structure, and then add some specific data members according to their own needs. For example, Gcclosure adds a callback untyped data pointer that points to the callback function that corresponds to the closure.

The gclosure structure contains many members, of which the three members that are more important to the user are marshal, data, and Marshal_data, where Marshal is a function pointer to the caller of a callback function; Data and Marshal_data are gpointer pointer types. For the C-language closure, data points to the user's address to the callback function, while marshal_data points to the callback function . I know, this sentence may cause the following question and answer.

Q: The gcclosure struct does not already have a callback pointer to the callback function.

A: This is because callback points to a function that is the default callback function of the closure, and the GObject library allows you to freely switch the callback function, you can consider marshal_data as a switch, it can temporarily mask the gcclosure of callback point to the callback function, Instead, enable the callback function that Marshal_data points to. Afterwards, you can call the G_closure_set_meta_marshal function to mask the callback function that Marshal_data points to, so that the closure's callback function is switched to the callback function that callback points to. A specific example is given below to validate this assertion.

From the perspective of GObject Library users, our understanding of the role of the gclosure structure stops here. If you dig deeper like a document [1], it's like being an ordinary person dissecting any animal without the psychological qualities of a physician, nausea is unavoidable.

We turn the topic back to the code snippet in the main function that we just discussed. Look at the following code:

Gclosure *closure = g_cclosure_new (G_callback (Float_compare), &a, NULL);
G_closure_set_marshal (Closure, g_cclosure_user_marshal_int__void_void);

Together, they implement the data structure shown in the following figure:

Immediately thereafter, the closed packet closure as the first parameter, the floating-point variable B as the 2nd parameter, substituting the function compare, namely:

Compare (closure, &b);

Because the floating-point variable A is included in closure, and the floating-point comparison function float_compare is included, it is obvious that the Float_compare function can be used to compare the size of floating-point numbers A to B in the compare function. But in fact, in the compare function, we did not call the Float_compare function directly, but instead gave the task to the G_closure_invoke function.

Look at the backbone of the Compare function:

static void
Compare (Gclosure *a, void *b)
{
        ...
 
        G_closure_invoke (A, &return_value, 1,¶m_value, NULL);
        Gint r = g_value_get_int (&return_value);
 
        ... ...
}

Since the first parameter of the Compare function is a closure closure, it already contains the floating-point variable A and the callback function Float_compare. When we use closure as a parameter to the G_closure_invoke function, the latter invokes the G_closure_user_marshal_int__void_void function based on the marshal pointer in closure, and G_closur The E_user_marshal_int__void_void function is our own definition, and for the example of this article its skeleton is as follows:
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);
 
        ... ... ...
}

The code does nothing more than get the floating-point variable A, the data1, from the closure closure, and then get the floating-point variable b from the parameters of the G_cclosure_user_marshal_int__void_void function, that is, data2, and then from the The closure closure gets the callback function that the callback pointer points to, which is float_compare, which finally completes the comparison of the size of floating point A to B.

The entire process of a closure call is shown in the following figure:

The work of gclosure closures is tedious, and its main task is to insert two links in the process of calling the callback function, that is, the G_closure_invoke function and the g_closure_user_marshal_*__** function. The main purpose of this is to improve the flexibility of closures. Like a chain, if there are only 2 nodes, then it can only survive like a straight line segment, and if it is transformed into 4 nodes, it becomes a 3-time curve. Replace Marshal

As previously stated, there is a marshal_data pointer in the gclosure struct, which can also point to a callback function, and supersedes the callback function that the callback pointer points to in the gcclosure struct body. To fully illustrate this, we have made some modifications to the previous Gclosure example, as follows:

#include <math.h> #include <glib-object.h> void g_cclosure_user_marshal_int__void_void (Gclosure *closur
                                        E, Gvalue *return_value g_gnuc_unused,
                                        Guint n_param_values, const gvalue *param_values, Gpointer invocation_hint g_gnuc_unused, Gpointer Mars
                                                     Hal_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 (¶m_value, G_type_pointer);
 
        G_value_set_pointer (¶m_value, b);
        G_closure_invoke (Closure, &return_value, 1,¶m_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 (¶m_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; }

The modification of the above code is mainly to delete the example part about floating-point number comparison, then add a new function str_compare_new, and make a change to the code of the string comparison part in main function, focus on the following code:
/* Main function code snippet *
        /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);  

The first call to the Compare function, the closure function call chain of the terminal is the Str_compare function, while the second call compare function, the closure function call chain terminal is the Str_compare_new function, it again to invoke Str_compare implementation string comparison. This is because, before the second call to the Compare function, we set the Marshal_data pointer of the closure closure through the G_closure_set_meta_marshal function, which points to the Str_compare_new function. Thus, in the G_cclosure_user_marshal_int__void_void function, the code:
callback = (gmarshalfunc_int__void_void) (Marshal_data marshal_data:cc->callback);

The value of callback will be marshal_data, not cc->callback. how to produce g_cclosure_user_marshal_* function quickly and well.

The GLib Library provides a tool called Glib-genmarshal that generates a valid marshal code based on the function description information we give you. I use this tool to generate the G_cclosure_user_marshal_int__void_void function above.

First, prepare a text document, such as In__void_void.txt:

Int:void,void

Then, execute the command:
$ glib-genmarshal--body int__void_void.txt > INT__VOID_VOID.C

The G_cclosure_user_marshal_int__void_void function can be generated.

In addition, GObject also pre-defined a set of marshal functions that refer to all G_CCLOSURE_MARSHAL_ prefixes in the document [2]. Reference Documents

[1] disgusting gobject[part i][v0.1] [2] gclosure part of GObject manual


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.