Posted on Sat Jan 5, 2019
It is established that my brain is getting old… sigh! I need to adapt: I need some new scheme to organise ideas and complexity; I need some new abstraction; I need some new notation. In these situations: I am in danger of valuating positively what is new over what is more effective; I know, so I will be careful… right?!
Most likely this experiment will result in failure. But failure is… failure! So… I do it because I feel like it. Life is hard…
I started experimenting something new with my CCLibraries projects. I do not want to fight the language: I do not want to introduce syntaxes that are out–of–place in a C language source base. I will just abuse a little the preprocessor (it is my understanding that some people abuse it much more than what I want to do…). I want everything to be standard C11.
I want every struct
type to have one or more of the following functions:
init()
Constructor function. Initialises an already allocated struct
instance: it can be on the stack
or embedded into another, enclosing, struct
. Acquires all the asynchronous resources associated
to the fields of the struct
.
final()
Destructor function. Finalises a struct
instance allocated on the stack or embedded into
another, enclosing, struct
. Releases all the asynchronous resources associated to the fields of
the struct
.
new()
Constructor function. Allocates a block of memory on the heap to hold a new struct
instance.
Acquires all the asynchronous resources associated to the fields of the struct
, usually by
calling the init()
function.
delete()
Destructor function. Finalises a struct
instance allocated on the heap. Releases all the
asynchronous resources associated to the fields of the struct
, usually by calling the
final()
function. Releases the allocated block of memory.
I have in mind more “well known” functions, with defined behaviour and defined type signature. There are also some “well known” variables, mostly tables of pointers to functions.
How do I name these functions and variables? Let’s say that a library’s namespace is my
and
a struct
is called coords
; the full type name is my_coords_t
. What’s the name of
the init()
function? One among:
my_coords_init my_init_coords init_my_coords my_coords_t_init
or another variant. I want a way to define such a function name that is both descriptive and easy
to replicate in different code repositories: every struct
in every code base I author must follow
the same naming convention, easily and recognisably. The fact that I cannot decide between the
first 3 of these alternatives, and just stick with it, is a sure sign of impending senility.
My ageing mind has thought of defining a preprocessor macro that is used as follows:
ccname_init(my_coords_t)
the result of expanding such macro use is a function name, for example:
my_coords_t__init
I will never see the actual name in the source code, I will always write the macro use. The actual function name will show itself in compiler messages: I will try to live with it.
Now let’s say we need two init()
constructors with different arguments: we have a rec
variant and a pol
variant; how do we generate the names for the init()
constructors?
Here:
ccname_init(my_coords_t, rec) ccname_init(my_coords_t, pol)
the first operand of the macro use is the struct
type name, the second operand is the name of the
constructor variant. Notice that, when appropriate, the ccname_
macros are variadic.
Let’s look at an actual definition and use:
typedef struct my_coords_t my_coords_t; struct my_coords_t { double X; double Y; }; void ccname_init(my_coords_t, rec) (my_coords_t * self, double x, double y) { self->X = x; self->Y = y; } void ccname_init(my_coords_t, pol) (my_coords_t * self, double rho, double theta) { self->X = rho * cos(theta); self->Y = rho * sin(theta); } int main (void) { my_coords_t A[1]; my_coords_t B[1]; ccname_init(my_coords_t, rec)(A, 1.0, 2.0); ccname_init(my_coords_t, pol)(B, 2.0, 1.0); exit(0); }
I want to define struct
types that act as “interfaces” for the struct
types representing
data. So an instance of my_serialisable_I
implements a serialisation api for a
specific instance of type my_coords_t
. An interface struct
is little more than a table
of pointers to function implementing the specialised api.
What is the name of the “well known” constructor for my_serialisable_I
acting upon an
instance of my_coords_t
?
ccname_iface_new(my_serialisable_I, my_coords_t)
If the interface has a write()
“method”: what is its name?
ccname_iface_method(my_serialisable_I, my_coords_t, write)
An the name of the struct
representing the table of pointers?
ccname_iface_table(my_serialisable_I, my_coords_t)
And if there are multiple variants of serialisable interface for the same my_coords_t
?
ccname_iface_new(my_serialisable_I, my_coords_t, rec) ccname_iface_new(my_serialisable_I, my_coords_t, pol)
Given that a struct
’s api is partly composed by functions with a well defined role: building
names with macros tells us directly which role a given function has. The definition:
my_coords_t const * ccname_new(my_coords_t, rec) (cce_destination_t L, double X, double Y) { ... }
tells us that the function is the rec
variant of a constructor for the type
my_coords_t
; the function call in:
cce_location_t L[1]; my_coords_t const * A; ... A = ccname_new(my_coords_t, rec)(L, 1.0, 2.0); ...
tells us that we are calling the rec
variant of the constructor for the type
my_coords_t
.
Let’s say we rename the type my_coords_t
to your_coords_t
; by just using the
editor’s facilities to replace strings we automatically change:
ccname_init(my_coords_t)
into:
ccname_init(your_coords_t)
there is no need to perform another pass to replace my_coords_init()
into
your_coords_init()
, and so on for the other functions in the api.
I use gnu Texinfo for my documentation needs. How do I document a function whose name is hidden and accessible only as the output form of a macro use? Given a function name that is built with:
ccname_init(my_coords_t, rec) ccname_init(my_coords_t, pol)
here is an attempt using Texinfo’s @deftypefun
environment (wrapping the macro use between
braces in the source file):
void
ccname_init(my_coords_t, rec) (my_coords_t * SELF, double X, double Y)
¶void
ccname_init(my_coords_t, pol) (my_coords_t * SELF, double RHO, double THETA)
¶Initialise an already allocated struct
instance using rectangular or polar coordinates.
Technologically it works; index search when browsing the documentation on the terminal with the Info reader also works (which is of paramount importance for me). How does it appear in the documentation browser? Long and stuffy, which makes it a pain for the eyes to scan the page; but descriptive and readable function names are also long and stuffy (raise your hand if you find LAPACK function names to be readable, easy to type without mistakes, easy to remember).
I use gnu Emacs for my editing needs and the gnu Autotools to manage building
projects. How do I customise a feature like complete-symbol
to auto–complete a macro–built
name? How do I customise a feature like xref-find-definitions
to search the definition point
of a function whose name is macro–built?
gnu Automake has built–in support for creating a TAGS file that we can use from Emacs
with complete-symbol
and xref-find-definitions
; these features work fine with
identifiers defined in C language code. But the following are not identifiers:
ccname_init(my_coords_t, rec) ccname_init(my_coords_t, pol)
the symbols ccname_init
and my_coords_t
are automatically picked up by the tags
infrastructure, but the whole macro–use needs customisation.
Using gnu Automake’s support for tags, we can add the following variable definition in Makefile.am:
AM_ETAGSFLAGS = --regex='{c}/"\\_<\\(ccname_\\(?:alloc\\|delete\\|final\\|i\\(?:face_\\(?:method\\(?:_type\\)?\\|new\\|table\\(?:_type\\)?\\)\\|nit\\)\\|method\\(?:_type\\)?\\|new\\|\\(?:releas\\|tabl\\(?:e_typ\\)?\\)e\\)\\)\\_>"/'
where the regular expression was generated under Emacs with:
(regexp-opt '("ccname_alloc" "ccname_delete" "ccname_final" "ccname_iface_method" "ccname_iface_method_type" "ccname_iface_new" "ccname_iface_table" "ccname_iface_table_type" "ccname_init" "ccname_method" "ccname_method_type" "ccname_new" "ccname_release" "ccname_table" "ccname_table_type") 'symbols)
Et voilà! Everything works! Most likely, in future, I will further develop and refine this regular expression.
Nowadays every source code editor has features for syntax highlighting, or font locking in gnu Emacs jargon. My current, imperfect, setup, loaded from .emacs, is this:
(defconst my-ccnames-macros (eval-when-compile (regexp-opt '("ccname_alloc" "ccname_delete" "ccname_final" "ccname_iface_method" "ccname_iface_method_type" "ccname_iface_new" "ccname_iface_table" "ccname_iface_table_type" "ccname_init" "ccname_method" "ccname_method_type" "ccname_new" "ccname_release" "ccname_table" "ccname_table_type") 'symbols))) ;;We perform this call to `font-lock-add-keywords' at the top-level, so ;;the configuration is done only once at file-loading time. ;; (font-lock-add-keywords ;;This argument MODE is set to `c-mode' because this call is ;;performed at the top-level. See the documentation of ;;`font-lock-add-keywords' for details. 'c-mode ;;Here we need to remember that "(regexp-opt ... 'symbols)" encloses ;;the generated regular expression between '\_<\(' and '\)\_>' so the ;;SUBEXP number must be 1 to match the actual symbol. ;; `(... ;;Abuse the keyword face to fontify some CCNames macro names. ;; ;;We use t as OVERRIDE argument to override an already existent ;;fontification with this specification. (,my-ccnames-macros 1 font-lock-keyword-face t) ...) ;;This true value as HOW argument causes this specification to be ;;appended to the value of `font-lock-keywords'. ;; ;;We need it to allow correct fontification of known function names, ;;which must happen after the fontification built into `c-mode'. t)
This needs further development, and coordination with other fontification specifications I use for C mode.
There is really nothing we can do about this!
What is the cognitive load required to write an read code like this? Less or more than plain C? I have no answer yet.