_get_ref_function() for none gobject classes like GtkExpression

For GObject classes we have g_object_info_get_ref_function() and g_object_info_get_unref_function()

https://developer.gnome.org/gi/stable/gi-GIObjectInfo.html#g-object-info-get-ref-function

For none GObject classes like GtkExpression the ref and unref funcions are listed in the XML file

    <class name="Expression"
           c:symbol-prefix="expression"
           c:type="GtkExpression"
           abstract="1"
           glib:type-name="GtkExpression"
           glib:get-type="gtk_expression_get_type"
           glib:fundamental="1"
           glib:ref-func="gtk_expression_ref"
           glib:unref-func="gtk_expression_unref"
           glib:set-value-func="gtk_value_set_expression"
           glib:get-value-func="gtk_value_get_expression">

But I can not find a function in the gobject-introspection API to get the ref function like “gtk_expression_ref”.

So would the only way to find it be scanning all the available functions/methods of the class/struct/union and grep the function by name containing “ref”?

[EDIT]

Actually I get the feeling that GtkExpression is the only none GObject based class that supports ref() and unref() operation currently? Well then it is not really hard to find the matching functions.

GIObjectInfo is not exclusive to GObject: it covers all classed types that inherit from GTypeInstance. That’s why the ref/unref and set/get-value data is attached to it.

The documentation in libgirepository is, sadly, out of date.

Nope, there’s also:

  • GskRenderNode
  • GdkEvent

which are GTypeInstance derived types with ref/unref functions.

Yes, indeed there are some more like GskTransform with functions like

https://developer.gnome.org/gsk4/stable/gsk4-GskTransform.html#gsk-transform-rotate

with first parameter GskTransform *next and annotation [allow-none][transfer full]. Because of “transfer full” we have to ensure that the Nim Garbage Collector does not try to free() the next parameter. I think we should do that best by calling gsk-transform-ref().

And for finding the ref functions we may need code like

proc findRefFunctionName(info: GICallableInfo): string =
  if gBaseInfoGetType(info) == GIInfoType.Object:
    if gObjectInfoGetFundamental(info) == GFalse:
      result = "g_object_ref"
    else:
      if moduleNameSpace == "gtk" and gBaseInfoGetName(info) == "Expression":
        result = "gtk_expression_ref"
  elif gBaseInfoGetType(info) == GIInfoType.STRUCT:
    if moduleNameSpace == "gsk" and gBaseInfoGetName(info) == "Transform":
      result = "gsk_transform_ref"

which we have to extend for some other types.

Don’t do pattern matching: the type data has the ref/unref functions. If it doesn’t, then you should file a bug against the library providing the introspection data.

For boxed types, like GskTransform, use g_boxed_copy() and g_boxed_free().

Yes, for boxed types I am using g_boxed_free(). But I think there is not g_boxed_unref().

The point is that I need a ref() in some rare cases when a function with “transfer full” takes over the function argument. The actual example to which someone pointed me recently was from listview_clocks.c the line

  expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
                                            gtk_expression_ref (clock_expression),
                                            "time");

That gtk_expression_ref() we would not want to use manually in higher level languages like Nim. gtk_expression_bind() is another example from that file with “transfer full”.

I simplified strategy, which is currently used in the Nim GTK bindings, is to make the Nim Garbage-Collector just to ignore that entity after a “transfer full”. That works, but the user should not continue using that entity then. But in the listview_clocks.c example program there are use cases where ref() is called on GtkExpression so that the argument can be used further even with “transfer full”.

So calling ref() seems to be the best solution, and I have to somehow find out the exact ref() function name.

For GtkExpression I found no straight gobject-introspection way to get the function gtk_expression_ref().

It doesn’t matter: reference counted boxed types will set their copy function to be a ref(), and their free function to be unref(); for instance:

G_DEFINE_BOXED_TYPE (GskTransform, gsk_transform,
                     gsk_transform_ref,
                     gsk_transform_unref)

Don’t fixate on what you think the semantics of the type are: use the provided API.

Just like I told you at the very beginning: g_object_info_get_ref_function() and g_object_info_get_unref_function() will return the ref/unref function for any classed type—i.e. anything that inherits from GTypeInstance.

Again: stop try to reverse engineer the behaviour from what you think the API does.

Sorry, your first post was not that clear to me. English is not my native language, and I am not a GTK/GObject veteran :slight_smile:

I have still no idea what these “copy function” is and when it is used. For instance I have a variable which is a pointer to a GtkExpression, and I pass that pointer to gtk_expression_bind()

https://developer.gnome.org/gtk4/stable/GtkExpression.html#gtk-expression-bind

The first self parameter is the GtkExpression pointer, and in Nim and in C we pass it just to the function as we would do for an int or a float. In listview_clock.c like

  expression = gtk_expression_ref (clock_expression);
  /* Now create the widget and bind the expression to it. */
  picture = gtk_picture_new ();
  gtk_expression_bind (expression, picture, "paintable", picture);
  gtk_box_append (GTK_BOX (box), picture);

For the gintro Nim bindings it would be not that different, only that we have an additional Nim proxy object for the GtkExpression. The Nim proxy object is some form of supervisor, when the proxy is finalized or destroyed, it call unref() on the GtkExpression. (Well for true gobjects like widgets it is a bit more complicated due to the toggle_ref stuff).

Currently I do not see where a special copy() function is involved.

[EDIT]

Well we have

Would you suggest that we call g_boxed_copy() on each parameter that we pass to functions? I don’t think so?

[Edit2]

Well maybe that is really intended: When we have a “transfer full” for a parameter of boxed type, and g_boxed_copy() is just a ref(), then we can do it?

But GtkExpression is not a boxed type, so what would we use for GObjects as a equivalent for g_boxed_copy()? Is there a g_gobject_copy()? Ah, use g_object_info_get_ref_function() to get the ref() function for the none boxed gobjects then? Maybe.

Actually for functions like gsk_transform_rotate()

GskTransform: GSK 4 Reference Manual

I can see no other way than guessing the ref() function. GskTransform is not a GObject and not a boxed type, but gsk_transform_rotate() is annotated with “transfer full” for that parameter. So we can guess the ref() function and ref() it, so we can continue using it further, or only detach the Garbage-Collector from it. But for the later the user should not use it any longer, which we can not enforce.

A “copy” function for a GBoxed type is the function to be called when creating a copy of a boxed type. What a boxed type considers “a copy” is entirely implementation-defined: some types perform a full clone (copy the data, recursively); some types perform a shallow clone (copy the container but not the data); some types use a reference count.

You should use g_boxed_copy() every time you have to transfer the ownership of a boxed type—i.e. for transfer full arguments and return values, you must perform a copy so that your wrapper type can be collected at the end of the scope.

There is no such thing as a “none boxed gobjects”.

Let’s try to sum up things a bit more simply, because you keep ignoring what I write and try to reverse engineer the thing I’m literally explaining to you:

  1. GBoxed types are plain old data structures with a GType associated to it; they have a copy and and a free function
  2. fundamental class types that inherit from GTypeInstance have a ref and an unref function attached to the GIObjectInfo
  3. classed types that inherit from GObject use g_object_ref() and g_object_unref()

Whenever you have to transfer ownership, you call:

  1. g_boxed_copy(), for a GBoxed type
  2. the specified ref function, for a classed type
  3. g_object_ref(), for a GObject type

There is never a case for “guessing” the memory management functions for a type in the GObject type system and in the introspection data. The entire point of the type system is to provide you with enough information to perform memory management of types; for newly added fundamental types, the introspection data complements the run time type information.

GskTransform is a boxed type.

2 Likes

Thanks for all your explanations, I will read it tomorrow again more carefully, too tired currently :slight_smile:

Indeed GskTransform is a boxed type, I was confused because for some other boxed types like GtkTreeIter there is a type cascade listed with GBoxed on top. For GskTransform I did not saw that, sorry.

GtkTreeModel: GTK 4 Reference Manual

Yes, I think now I understand your explanations.

I have now this code fragment

      var refFunc: string = "" # default
      if gCallableInfoGetInstanceOwnershipTransfer(info) == GITransfer.EVERYTHING:
        if gBaseInfoGetType(binfo) == GIInfoType.Object:
          if gObjectInfoGetFundamental(binfo) == GFalse:
            refFunc = "g_object_ref("
          else:
            refFunc = $gObjectInfoGetRefFunction(binfo) & '('
        elif gRegisteredTypeInfoGetGType(binfo) != G_TYPE_NONE:
          if gTypeFundamental(gRegisteredTypeInfoGetGType(binfo)) == G_TYPE_BOXED:
            var getTypeProc = $gRegisteredTypeInfoGetTypeInit(binfo)
            refFunc = "g_boxed_copy(" & getTypeProc & "(), "

which generates for gtk_expression_bind()

proc gtk_expression_bind(self: ptr Expression00; target: ptr gobject.Object00;
    property: cstring; this: ptr gobject.Object00): ptr ExpressionWatch00 {.
    importc, libprag.}

proc `bind`*(self: Expression; target: gobject.Object; property: cstring;
    this: gobject.Object = nil): ExpressionWatch =
  fnew(result, gBoxedFreeGtkExpressionWatch)
  result.impl = gtk_expression_bind(cast[ptr Expression00](gtk_expression_ref(self.impl)), cast[ptr gobject.Object00](target.impl), property, if this.isNil: nil else: cast[ptr gobject.Object00](this.impl))
  result.ignoreFinalizer = true

Looks OK for me and compiles.

This is for the case of instance parameters of methods. I will test that, and when it works apply to other function parameters with “transfer full” annotation then also.

i.e. for transfer full arguments and return values,

For “return values” I am still a bit unsure, I have to find a concrete function where a g_boxed_copy() would make sense. But at least now I know how to do it.

But wait, gtk_expression_bind() has annotation “transfer none” for the GtkExpressionWatch return value. Maybe in that case I should do exactly the same, that is applying g_boxed_copy() on the return value when it is boxed type.

IMO it is a little unfortunate that these functions are known as ‘copy’ functions because they are really ‘duplication’ functions, and the name g_boxed_dup would would have been more consistent with the names of well known C functions.

Did you mean to say “i.e. ‘in’ arguments that are transfer full, ‘out’ arguments and return values that are transfer none, etc.”?

I’m not sure what the scope of this discussion is but it may also be worth noting that there are struct types (as described by record elements in a GIR file) that are not boxed (the record element has no glib:get-type attribute), e.g. GtkAccelKey. Such structs are (hopefully) ‘flat’ so g_malloc()/memcpy()/g_free() can be used.

Also, that’s not the full story for struct types due to C arrays. For example, the ‘out’ argument colors of the function gtk_color_selection_palette_from_string () is an array where the GdkColor structs are inline so, even though GdkColor is a boxed type, you can’t use g_boxed_free to clean up the array whose ownership you received. (Ok, this particular type is deprecated.) Similarly, if there was an example of an ‘in’ argument with transfer ‘full’ whose type was an inline C array of a boxed type, you couldn’t use g_boxed_copy () because you need an actual copy operation (in the sense of memcpy) for each array element, not a duplication operation like g_boxed_copy ().

1 Like

There may be some problems left unfortunately. One is

grep -A5 "record name=\"PrintBackend" /opt/gtk/share/gir-1.0/Gtk-4.0.gir 
    <record name="PrintBackend" c:type="GtkPrintBackend" disguised="1">
      <source-position filename="../gtk/gtkprinter.h" line="80"/>
    </record>

and function gtk_printer_get_backend()

GtkPrinter: GTK 4 Reference Manual

which returns a GtkPrintBackend with [transfer none].

Maybe that is one of the exceptions Phil Clayton talked about.

Interesting is that from

https://developer.gnome.org/gtk4/stable/GtkPrinter.html#GtkPrinter.object-hierarchy

GtkPrintBackend is listed under GObject.

GtkPrintBackend is a GObject, but it’s also an internal detail of GTK. In practice, it has no public type or API.

The next version of the GTK API reference will hide the only use of GtkPrintBackend, which is the GtkPrinter:backend property.

1 Like

It isn’t - what to do with a record element with the attribute disguised="1" is another matter that I didn’t mention. However, in this case, I would expect GtkPrintBackend to be described by a class element in the GIR file as it is a GObject.

1 Like

Next one:

For GtkExpressionWatch we have a ref function gtk-expression-watch-ref

GtkExpression: GTK 4 Reference Manual

which we would need to ref the return value of gtk_expression_bind().

So I intended to read it from gobject-introspection but failed. Reason may be that it is a record

    <record name="ExpressionWatch"
            c:type="GtkExpressionWatch"
            glib:type-name="GtkExpressionWatch"
            glib:get-type="gtk_expression_watch_get_type"
            c:symbol-prefix="expression_watch">

While GtkExpression is class

    <class name="Expression"
           c:symbol-prefix="expression"
           c:type="GtkExpression"
           abstract="1"
           glib:type-name="GtkExpression"
           glib:get-type="gtk_expression_get_type"
           glib:fundamental="1"
           glib:ref-func="gtk_expression_ref"
           glib:unref-func="gtk_expression_unref"
           glib:set-value-func="gtk_value_set_expression"
           glib:get-value-func="gtk_value_get_expression">

[EDIT]

But well, maybe GtkExpressionWatch is a g_boxed type, which my current evaluation logic does not show. I may have to rearrange my if conditions…

[EDIT 2]

Yes, GtkExpressionWatch is a g_boxed type, so we should finally get it working :slight_smile:

If a record element has a glib:get-type attribute, I treat it as a boxed type. I think that is valid. In this case, the code confirms it.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.