Class members that aren't vfuncs

Going over the documentation, I always see that *Class structs contain either parent, vfuncs, or reserved space. Being C of course, there’s nothing limiting the fields to those types, and they could be simple types (ints, double, gchar*, etc.) instead. For example, in C I can do this:

struct _LibraryObjectClass {
        /*< private >*/
        GObjectClass parent_class;

        /*< public >*/
        gchar *class_member;

        /*< private >*/
        gpointer reserved[4];
};

and assign class_member in class_init. Looking at the GObject introspection data, that information is clearly parsed and preserved in the .gir:

    <record name="ObjectClass"   
            c:type="LibraryObjectClass"
            glib:is-gtype-struct-for="Object">
      <source-position filename="../lib/mylib-0.h" line="52"/>
      <field name="parent_class" readable="0" private="1">
        <type name="GObject.ObjectClass" c:type="GObjectClass"/>
      </field>
      <field name="class_member">
        <type name="utf8" c:type="gchar*"/>
      </field>
      <field name="reserved" readable="0" private="1">
        <array zero-terminated="0" fixed-size="4">
          <type name="gpointer" c:type="gpointer"/>
        </array>
      </field>
    </record>

In C, I can create subclasses which set this field, and then access them in functions, but I cannot figure out how to do this from languages using GIR bindings.

For example, Vala docs mention instance, class, and static fields, but if I mark a field as class, it does not set the LibraryObjectClass->class_member field. I tried to do a similar thing in Python with PyGObject, but there doesn’t seem to be any magic to setup class fields (that I could find in the implementation.)

Is this actually something that’s supported? Have bindings simply never encountered or needed to implement it? Or is this just not something that’s supposed to be done. The docs simply never mention non-vfunc fields, either for or against.

You can find a full example here:
https://gitlab.gnome.org/QuLogic/gir-test

Language bindings don’t typically expose the “class” structure for a variety of reasons—mainly because the concept itself may not make sense at all.

If the language binding you’re using has a concept of “class”, it’s more recommended to use a function to set/get the data on a class structure field, instead of direct access. Additionally, you may want to use a private class data structure, which would allow you to retain ABI compatibility in case you need to add or remove class-wide fields.

Perhaps, but the language examples I gave both do have such concepts.

How would one name this for it to be exposed correctly? Keep in mind it needs to work on the class, not random instances.

Not possible since it’s derivable.

I can’t speak for python, but in Vala this would be a class field (not a static field). However, as you can see from the generated vapi, vapigen is not picking up the field from the gir.

The declaration, if you were to write the vapi by hand, would be something like:

   public class string class_member;

This was probably not picked up by vapigen as it is almost never used in libraries.

Ah yes, you are right. I’m not sure why I committed it as static, but I did try class as well. I’ve updated the example, but as you say it was not applied.

Should I report a bug in vapigen?

Oh, actually, I never looked at the generated code with class. This one is more correct, in that it produces:

struct _AppObjectClass {
        LibraryObjectClass parent_class;
        gchar* class_member;
};

i.e., it’s at least on the Class. So if vapigen could correctly pick this up, it would probably translate correctly.

Actually, would that even work? If I manually edit the vapi to add public class string class_member; then the translated C code still contains its own class_member, so doesn’t override the existing version. There is a new warning:

[1/3] Compiling Vala source vala/mylib.vapi ../vala/app.vala.
../vala/app.vala:2.2-2.33: warning: AppObject.class_member hides inherited field `Library.Object.class_member'. Use the `new' keyword if hiding was intentional
	public class string class_member = "Vala Class member";
	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Compilation succeeded - 1 warning(s)

But if I add new, it just silences the warning. It doesn’t override the existing field.

An extant example of this is in libsoup, with the schemes field of SoupRequest.