Documenting virtual methods in a class

I wondered how to document virtual methods in a class and looked around gio, since this is a prime example of GObject (and gtk-doc) usage.

In doing so, I noticed the following:

1.) Virtual methods in a class structure seem to be rarely used. Instead, types like GFile and GAction use custom type interfaces. Is there a general shift towards type interfaces rather than virtual methods in a class? Obviously, documenting a type interface like GFileIface is straight-forward and also correctly showing up in the general documentation, so I guess that this is the preferred method of implementing and using virtual functions nowadays?

2.) Some classes are still using virtual methods, but only a few seem to actually document functions (and even if they do, most of the time only some of them). I’ve found GResolver as an example of that. The caveat there is that the virtual methods are documented using the Class::function syntax, which, according to the gtk-doc manual on documentation syntax, is the syntax for a signal. That’s misleading in this case, since the documentation is for a different concept - virtual methods - and not signals. (Yes, implementation-wise, both signals and virtual methods are the same, but not conceptually.)

3.) Even for those documented virtual methods, the documentation doesn’t show up anywhere on the web. For instance, I tried to find the resulting documentation for this paragraph:

* This is identical to GResolverClass::lookup_by_name except it takes
* @flags which modifies the behavior of the lookup. See #GResolverNameLookupFlags
* for more details.

via a simple web search for "except it takes flags which modifies the behavior of the lookup." (quotes are literal and part of the search query, of course, because we’re interested in text exactly appearing like this), but it turned up no relevant results. It’s definitely not part of the GResolver documentation, but I was curious to find out if there was more specific documentation for GResolverClass, which doesn’t seem to be the case.

So, currently, it looks like there’s no proper way to document virtual class methods. Is that a bug in gtk-doc? gtk-doc does seem to parse documentation for signals alright and publish it, but stops after finding a comment like /* Virtual methods */.

Using a type interface, the contained methods can be documented (a bit) with a top-level comment and the documentation is published in a sane manner by gtk-doc. Is this the way to go for new code rather than embedding virtual functions in the class structure?

Even then, there is a problem with documenting type interfaces. You can’t really document functions properly that way. Giving a short description is possible, but it’s impossible to document parameters, return types or even write up a nice long description.

So, really, no solution is really satisfactory. Documenting the functions inline (like in the virtual-methods-class example) is really the best solution, but it’s a bit useless if the documentation is not being generated/published anywhere. Mixing inline documentation with a type interface would sound interesting, but… the inline documentation probably won’t show up either.

1 Like

That’s the case in GIO for many things but not generally the pattern. Interfaces are interfaces, classes are classes. They both have their use-cases and should be used wherever either pattern makes more sense.

If you take a look at GTK or GStreamer, you’ll see a mix of both depending on what the type is good for.

This seems to be done inconsistently in the documentation currently. Some places document it like struct members, others like signals. Struct-member-style seems to be parsed by gtk-doc but does not allow for putting annotations on individual parameters. Signal-style allows for annotations and seems to be parsed by gobject-introspection and hotdoc just fine. As such, signal-style seems to be the correct way as it’s more expressive.

I might be wrong here though, the documentation syntax is often confusing to me too :slight_smile: Maybe @ebassi can give the authoritative answer here.

Note that this is not just important for textual documentation but also for all the annotations that are crucial for language bindings, so getting the annotations to show up correctly is rather important.

This is not something that can really be changed at this point as it would break all existing documentation so we’ll have to live with what we have right now, possibly adding a new clearer way for documenting virtual methods and deprecating the old ones.

You should have checked GTK, instead. :slight_smile:

GIO uses interfaces because it relies on the factory design pattern a lot, given the GIO/GVfs split. GTK, on the other hand, has a deeper type hierarchy, and more abstract base classes.

In general, virtual functions are mostly used through wrappers; the only reason why we document virtual functions is to allow annotations on their parameters and return values, for language bindings.

No. The documentation for a signal is: TypeName::signal-name; the documentation for the virtual function is ClassStructure::virtual-function. There’s no ambiguity, there, unless you somehow manage to call your type ClassStructure, which would be weird and not really idiomatic; in which case, you get to keep both the pieces when something breaks.

Yes, there can be some confusion. Sadly, gtk-doc isn’t really maintained: being a massive Perl script for nearly 15 years meant very few people felt comfortable working on it, or getting acquainted with the code base; and then being ported to Python, kinda broke it. That’s the sad reality of infrastructure software that keeps working until one day it stops.

That’s because gtk-doc is not using the introspection data—which contains the doc annotation for virtual functions; instead, it parses the C header and source files, and it doesn’t know how to deal with the virtual function annotations.

There is a way: you should have real functions that call virtual functions, and you should add implementation notices there. The only real reason why you should annotate virtual functions, as I said above, is for language bindings, to ensure they can deal with ownership transfer, direction, and nullability of parameters and return values. If you also want to drop a description that’s absolutely fine, but it should not be the only place where it’s written down.

3 Likes

Note that not every virtual method has a public caller function though, some are only used internally inside the class and are not supposed to be called from the outside. In that case the virtual method documentation is the only thing that exists.

Unlike gtk-doc, hotdoc also handles those AFAIU, and is based on the introspection data anyway.

1 Like

If there’s no actual caller, then it’s not really public API. Getting the class/interface vtable and dereferencing a function pointer isn’t what I classify as “an actual API”.

1 Like

That’s what I’m saying, yes. In Java/C# terms that would be (close to) a protected virtual method: subclasses can override it but nobody should call it from the outside.

Documentation of those is still useful for implementers of subclasses and to get proper annotations.

2 Likes

Right. I often forget about GTK (and GStreamer) because it’s so convenient to look at glib’s code, especially since I often have to look up stuff directly to better understand what it does (and, frankly, because GTK is so huge and complex).

Ah, that’s definitely good to know. I haven’t seen that syntax mentioned in the gtk-doc documentation, so was unaware of that, but it certainly does make sense and seems to be used in places. So, quite often it’ll just be TypeNameClass::virtual-method. I probably should make a mental note to send in a patch documenting this in gtk-doc.

Oh, I know this too well. So the only real way to fix that is to… fix gtk-doc so that it parses virtual functions documentation. At the very least, I should probably create a bug report to track this.

I generally do that in my abstract base classes, but so far have mostly linked to the virtual method documentation for the actual expected behavior. I guess I should just write that up in both places for now.

Since the global-scope wrapper functions generally should feature the same properties (ownership etc.), that might not actually be a huge issue - if developers using bindings remember to look up the documentation for the wrapper functions, that is.

Yep, that can be useful to inheritors of virtual base classes. I believe that even internal (for the lack of a better term, “shared”?) API should be documented for direct users of affected classes, even if the general public won’t be able (or also is not supposed) to use it.

Thanks, everything here has been very helpful!

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