Holger
Holger

Reputation: 1020

Gtk4 GObject, ClosureMarshal how can I use "return value"?

"A GClosure represents a callback supplied by the programmer.

It will generally comprise a function of some kind and a marshaller used to call it. It is the responsibility of the marshaller to convert the arguments for the invocation from GValues into a suitable form, perform the callback on the converted arguments, and transform the return value back into a GValue." This is how it is in the documentation.

To test and try it out, I wrote a small program that is intended to use the following structure:

struct GClosure {
  guint in_marshal : 1;
  guint is_invalid : 1;
  void (* marshal) (
    GClosure* closure,
    GValue* return_value,
    guint n_param_values,
    const GValue* param_values,
    gpointer invocation_hint,
    gpointer marshal_data
  );
}

My problem is that I have not yet managed to find a way to use "return-value".

Unfortunately, all my previous attempts ended with a memory error.

Here is the Script:

 
#include<gtk/gtk.h>

typedef struct _MyClosure 
{
    GClosure closure;
    int closure_data;
} MyClosure;

static void
my_closure_finalize(gpointer notify_data, GClosure *closure)
{
    MyClosure * my_closure = (MyClosure*)closure;
    printf("Finalizing closure with closure_data: %d\n",my_closure->closure_data);
}

GValue* my_callback(GClosure *closure, const GValue *param_values)

{   
    MyClosure *my_closure = (MyClosure*)closure;
    printf("In Callback with closure_data. %d\n", my_closure->closure_data);
    if (&param_values[0] != NULL) 
    {
    printf("In Callback with value: %s\n", g_value_get_string(&param_values[0]));
    }
        /* Return */
    GValue value = G_VALUE_INIT;
    g_value_init(&value, G_TYPE_STRING);
    g_value_set_static_string(&value, "The End");
    /* edit */
    printf("Return from Callback %s\n",g_value_get_string(&value));
    return value;

}

static void
my_closure_marshal(GClosure *closure, 
        GValue *return_value, 
        guint n_params_values, 
        const GValue *param_values, 
        gpointer invocation_hint, 
        gpointer marshal_data)
{
    MyClosure *my_closure = (MyClosure*)closure;
    printf("mashalling closure with closure_data. %d\n", my_closure->closure_data);
    if (&param_values[0] != NULL) 
    {
        printf("Callback called with value: %s\n", g_value_get_string(&param_values[0]));
    }
   /* edit */    
  *return_value = my_callback(closure,param_values);
        // Segmentation fault (core dumped)
        /*
        printf("after Callback\n");
        GType type = G_VALUE_TYPE(return_value);
        g_print("Return type %s\n",g_type_name(type));
        g_print("Result in Mashaller %s\n",g_value_get_string(return_value));
        */

}   

MyClosure *
my_closure_new(gpointer data)
{
    GClosure *closure;
    MyClosure *my_closure;
    closure = g_closure_new_simple(sizeof(MyClosure), data);
    my_closure = (MyClosure*)closure;
    my_closure->closure_data = 42; 
    g_closure_add_finalize_notifier(closure,NULL, my_closure_finalize);
    g_closure_set_marshal(closure, my_closure_marshal); 
    return my_closure;
}

int main(int argc, char* argv[])
{
    MyClosure *my_closure = my_closure_new(NULL);
       
    GValue* return_value;
    GValue value = G_VALUE_INIT;
    g_value_init(&value, G_TYPE_STRING);
    g_value_set_string(&value, "Hello");

    g_closure_invoke((GClosure*)my_closure,return_value, 1,&value , NULL);

    /* How can I further process here "return_value" */
    /*It should be the string "The End". */
    
    g_closure_unref((GClosure*)my_closure);

    return 0;
}

Does anyone have an idea?

Having edited according to the proposal of 'Bodo', I get the following result:

mashalling closure with closure_data. 42
Callback called with value: Hello
In Callback with closure_data. 42
In Callback with value: Hello
Return from Callback The End
Segmentation fault (core dumped)

Upvotes: 1

Views: 70

Answers (2)

Holger
Holger

Reputation: 1020

After many attempts, I have now succeeded in creating a test program for using GClosure that no longer generates errors.

In addition to the indications from @bodo, it was still important, return_value before calling g_closure_invoke((GClosure*)my_closure,return_value, 1,&value , NULL); with GValue * return_value = g_new0(GValue, 1);to create.

The whole script now looks like this:

#include<gtk/gtk.h>

typedef struct _MyClosure 
{
    GClosure closure;
    int closure_data;
} MyClosure;

static void
my_closure_finalize(gpointer notify_data, GClosure *closure)
{
    MyClosure * my_closure = (MyClosure*)closure;
    printf("Finalizing closure with closure_data: %d\n",my_closure->closure_data);

}

gchar *
my_callback(GClosure *closure, const GValue *param_values)
{   
    MyClosure *my_closure = (MyClosure*)closure;
    gchar *result;
    printf("Callback use:\n");
    printf(" -- closure_data: %d\n", my_closure->closure_data);
    if (&param_values[0] != NULL) 
    {
        printf(" -- &value:       %s\n", g_value_get_string(&param_values[0]));
        result = g_strdup_printf("%s %d %s",g_value_get_string(&param_values[0]) ,  my_closure->closure_data, "The Ende");
    }
        else
        result = g_strdup_printf("%s" , "The Ende");

    /*Return*/
    return result;
}

static void
my_closure_marshal(GClosure *closure, 
        GValue *return_value, 
        guint n_params_values, 
        const GValue *param_values, 
        gpointer invocation_hint, 
        gpointer marshal_data)
{
    MyClosure *my_closure = (MyClosure*)closure;

    g_value_init(return_value,G_TYPE_STRING);
    
    gchar *result = my_callback((GClosure*)my_closure,param_values);

    g_value_set_string(return_value,result);

}   

MyClosure *
my_closure_new(int data)
{
    GClosure *closure;
    MyClosure *my_closure;
    closure = g_closure_new_simple(sizeof(MyClosure), &data);
    my_closure = (MyClosure*)closure;
    my_closure->closure_data = data; 
    g_closure_add_finalize_notifier((GClosure*)my_closure,NULL, my_closure_finalize);
    g_closure_set_marshal((GClosure*)my_closure, my_closure_marshal); 
    return my_closure;
}

int main(int argc, char* argv[])
{
    MyClosure *my_closure = my_closure_new(42);
       
    GValue * return_value = g_new0(GValue, 1);

    GValue value = G_VALUE_INIT;
    g_value_init(&value, G_TYPE_STRING);
    g_value_set_string(&value, "Hello");

    g_closure_invoke((GClosure*)my_closure,return_value, 1,&value , NULL);

    g_print("Result: %s\n",g_value_get_string(return_value));

    g_value_unset(&value);
    g_free(return_value);
    g_closure_unref((GClosure*)my_closure);
    
    return 0;
}

Upvotes: 0

Bodo
Bodo

Reputation: 9875

GValue* return_value means that this argument must be a pointer to a variable of type GValue. As with any pointer, to return a value you can't modify the pointer inside the function, but you have to store the value at the location the pointer points to.

Something like

GValue my_callback(GClosure *closure, const GValue *param_values)

{   
    MyClosure *my_closure = (MyClosure*)closure;
    printf("In Callback with closure_data. %d\n", my_closure->closure_data);
    if (&param_values[0] != NULL) 
    {
    printf("In Callback with value: %s\n", g_value_get_string(&param_values[0]));
    }
        /* Return */
    GValue value = G_VALUE_INIT;
    g_value_init(&value, G_TYPE_STRING);
    g_value_set_static_string(&value, "The End");

    return value;
}

static void
my_closure_marshal(GClosure *closure, 
        GValue *return_value, 
        guint n_params_values, 
        const GValue *param_values, 
        gpointer invocation_hint, 
        gpointer marshal_data)
{
    MyClosure *my_closure = (MyClosure*)closure;
    printf("mashalling closure with closure_data. %d\n", my_closure->closure_data);
    if (&param_values[0] != NULL) 
    {
        printf("Callback called with value: %s\n", g_value_get_string(&param_values[0]));
    }
    *return_value = my_callback(closure,param_values);
        GType type = G_VALUE_TYPE(return_value);
    g_print("Return type %s\n",g_type_name(type));
        g_print("Result in Mashaller %s\n",g_value_get_string(return_value));
}   

This code is untested.


The original function my_callback contains undefined behavior because it returns the address of the local variable value:

GValue* my_callback(GClosure *closure, const GValue *param_values)

{   
    /* ... */
        /* Return */
    GValue value = G_VALUE_INIT;
    /* ... */
    GValue *ret = &value;
    return ret;
}

Upvotes: 2

Related Questions