132 lines
4.1 KiB
ReStructuredText
132 lines
4.1 KiB
ReStructuredText
Emacs: Implement a GObject’s 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. That’s 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`.
|