Emacs: Implement a GObject’s virtual function

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.

links

social