gergelypolonkai-web-jekyll/content/blog/2016-01-13-emacs-implement-...

132 lines
4.1 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Emacs: Implement a GObjects virtual function
#############################################
:date: 2016-01-13T13:31:12Z
:category: blog
:tags: c,development,emacs
:url: 2016/01/13/emacs-implement-a-gobject-s-virtual-function/
:save_as: 2016/01/13/emacs-implement-a-gobject-s-virtual-function/index.html
:status: published
:author: "Gergely Polonkai"
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:
.. code-block:: c
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):
.. code-block:: c
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. Thats a cumbersome job for more than 40 function headers. But Emacs
comes to the rescue!
.. code-block:: lisp
(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 :kbd:`M-x implement-gobject-vfunc`, then put the same vtable entry somewhere before the
interface init function, and execute :kbd:`M-x implement-gobject-vfunc-prototype`.