Old dogs...

Sep. 8th, 2018 01:23 pm
pheloniusfriar: (Default)
[personal profile] pheloniusfriar
I have been using the C programming language for startlingly close to 40 years, but I had occasion today to use three macro definition features of the C pre-processor that I have never (to my memory anyway) used before.

I wanted to automate the declaration of groups of variables I use over and over again. The variables all have different data types, a common prefix, and different suffixes, so things like "char *BOB_name;", "int BOB_count;", etc.. The problem is that if I define a substitution macro with arguments, it requires that the tag have whitespace around it, so something like "#define argh(TAG) char *TAG_name; int TAG_count" doesn't do any substitution. However, there is a feature that tells the pre-processor to concatenate the tag and whatever comes after it, and that is the "##" token:
#define argh(TAG) char *TAG ## _name; int TAG ## _count
(having the "*" right before the tag doesn't seem to be a problem). So specifying:
argh(BOB);
gives:
char *BOB_name; int BOB_count;
which is what I wanted.

The second thing combined two features: variable argument lists and wrapping a tag in double-quotes (which make it a string). The first is because I want to use the macro to invoke an actual variable-argument function, and the second is because the pre-processor won't do substitutions inside a string, so the "#" token before a tag in the substitution text tells it to add double quotes around the tag and put it there. The double quotes thing works well because if you specify "foo" "bar" in C, the compiler concatenates the two strings together to make "foobar", so you can make one of initial strings the tag and a properly formatted string will be produced. For the variable arguments, this has been supported since C99, but there is a subtlety that is needed to handle the case for when there are 0 arguments specified. Unfortunately, this wasn't in any specification until C++2a and gcc 8 (I'm using gcc 4.8 still). If supported, all together, this looks like:
#define spooge(TAG, search, ...) TAG ## _data = \
  find(connection, flag, TAG ## _fields, #TAG, search, __VA_OPT__(,) __VA_ARGS__)
(where the backslash indicates that the macro is continued on the next line). If I then write the following:
spooge(BOB, "where location = '%s'", location);
it expands as:
BOB_data = find(connection, flag, BOB_fields, "BOB", "where location = '%s', location);
Because of the "__VA_OPT__(,)" token, it will only put a comma in the expansion if there are arguments specified. So specifying:
spooge(BOB, "where location = 'cat tree'");
will expand as:
BOB_data = find(connection, flag, BOB_fields, "BOB", "where location = 'cat tree'");
(note the lack of comma after the "search" tag substitution). Also note that the last bit of these macros is specified without a semicolon. This is so that when the macro is invoked like any other C statement with a semicolon at the end, the substitution happens and then there's that semicolon at the end already (if you put a semicolon at the end of the macro, there would be two after the substitution, ";;", but it would be fine as the second would just be ignored, but... there's an elegance about doing it this way).

Unfortunately (I never come here with nice, happy programming stories), since the version of the compiler I have doesn't support the "__VA_OPT__(,)" feature, it just means that I have to specify at least one argument for it to work. Luckily, I have lots of parameters, so I just need to stop one argument early so I always have the "search" string as at least one variable argument:
#define spooge(TAG, ...) TAG ## _data = \
  find(connection, flag, TAG ## _fields, #TAG, __VA_ARGS__)
So when I invoke:
spooge(BOB, "where location = '%s'", location);
it expands as:
BOB_data = find(connection, flag, BOB_fields, "BOB", "where location = '%s', location);
where the "where..." string is just another variable argument, but one that is guaranteed to be there! Also:
spooge(BOB, "where location = 'cat tree'");
will expand as:
BOB_data = find(connection, flag, BOB_fields, "BOB", "where location = 'cat tree'");
again, because the "where..." string is a variable argument parameter now. It is not as elegant from a macro definition standpoint, but it accomplishes the same thing for me so I'm happy (or as happy as I can be stuck in front of my monitor on a lovely day in Ottawa... maybe I'll take a stroll later today...).

The next main thing I need to do is decide on a documentation tool for the code. I used ROBOdoc extensively about a decade ago (and still like it), but it appears to have fallen out of favour and isn't being actively developed anymore. For some reason, I really don't like Doxygen... I'm not sure why. The two contenders that I'm looking most closely at are Sphinx (which is written in Python and seems to be a popular choice these days), and the "literate programming" paradigm implemented with CWEB (Donald Knuth and Silvio Levy's game-changing system from the 1980s where you write a "web" that can either be tangled to generate C code or weaved to generate documentation for processing by the TeX document formatting language). The latter is the more powerful paradigm as it is document's one's thinking as the program is written rather than just pretty-printing the comments from the code.

And for today's music video (hopefully giving some value-add to your reading page rather than endless technobabble notes to myself as I work). I love the synth-dancy creatures:

Profile

pheloniusfriar: (Default)
pheloniusfriar

May 2025

S M T W T F S
    123
45678 910
11121314151617
1819202122 2324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 12th, 2026 07:18 am
Powered by Dreamwidth Studios