I have recently started creating a GLib implementation of the Matrix.org API. For that, I have created a GObject interface, MatrixAPI, which has as many virtual functions as API calls (which is a lot, and expanding). This way I ended up with the following scenario.
In matrix-api.h I had a struct like this, with a lot more elements:
typedef struct {
void (*initial_sync)(MatrixAPI *api,
MatrixAPICallback callback,
gpointer user_data,
GError **error);
void (*sync)(MatrixAPI *api,
MatrixAPICallback callback,
gpointer user_data,
GError **error);
…
And in matrix-http-api.c, which implements MatrixAPI, I have a function like this (again, with a lot more elements):
static void
matrix_http_api_matrix_api_init(GObjectInterface *iface)
{
iface->initial_sync = i_initial_sync;
iface->sync = i_sync;
…
}
And every time I wanted to implement a new function from the vtable, I had to copy the prototype, and add an iface->foo_bar = i_foo_bar line and an actual function header for i_foo_bar with the same parameters. That’s a cumbersome job for more than 40 function headers. But Emacs comes to the rescue!
(require 'thingatpt)
(defun get-point(symbol &optional arg)
"Get point, optionally running a command beforehand"
(funcall symbol arg)
(point))
(defun copy-symbol-at-point()
"Copy the symbol under point"
(interactive)
(save-excursion
(let ((beg (get-point 'beginning-of-thing 'symbol))
(end (get-point 'end-of-thing 'symbol)))
(copy-region-as-kill beg end))))
(defun implement-gobject-vfunc()
"Change a vtable line of a GObject interface to an implementation line like:
void (*my_iface_func)(type1 param1, type2 param2, ...);
to
iface->my_iface_func = i_my_iface_func;"
(interactive)
(save-excursion
(let ((beg ((lambda()
(search-forward "(*")
(point))))
(end ((lambda()
(back-to-indentation)
(point)))))
(kill-region beg end))
(copy-symbol-at-point)
(insert "iface->")
(end-of-thing 'symbol)
(delete-char 1)
(let ((beg (point))
(end ((lambda()
(find-list-end)
(point)))))
(kill-region beg end))
(insert " = i_")
(yank 2))
(next-line)
(beginning-of-line))
(defun implement-gobject-vfunc-prototype()
"Change a vtable line of a GObject interface to an implementation prototype line like:
void (*my_iface_func)(type1 param1, type2 param2, ...);
to
static void
i_my_iface_func(type1 param1, type2 param2, ...)"
(interactive)
(let ((beg ((lambda()
(back-to-indentation)
(point))))
(end ((lambda()
(beginning-of-line)
(point)))))
(kill-region beg end))
(insert "static ")
(search-forward "(*")
(delete-char -3)
(newline)
(insert "i_")
(end-of-thing 'symbol)
(delete-char 1)
(let ((beg (point))
(end ((lambda()
(find-list-end)
(point)))))
(indent-region beg end))
(delete-char 1))
Now all I have to do is to copy the whole vtable entry into matrix_http_api_matrix_api_init(), execute M-x implement-gobject-vfunc, then put the same vtable entry somewhere before the interface init function, and execute M-x implement-gobject-vfunc-prototype.