- fun hacks
A lot of my JustForFun™ hacking happens late at night, often when I have trouble falling asleep. In a recent episode, I wanted to see if I could coax the C preprocessor into something JSX-like. Quite surprisingly, the Mastodon post got a lot of attention, so I wanted to preserve it here.

#The monstrosity
Anyway, here’s what I came up with:
#define _(...) __VA_ARGS__
#define html_(x) "<html>" #x "</html>"
#define html(x) html_(x)
#define tag(name, x) <name>x</name>
#define tag_attr(name, attrs, x) <name attrs>x</name>
#define tag_void(name, attrs) <name attrs>
#define attr(name, value) name = value
#define head(x) tag(head, x)
#define link(attrs) tag_void(link, attrs)
#define body(x) tag(body, x)
#define div(attrs, x) tag_attr(div, attrs, x)
#define h1(attrs, x) tag_attr(h1, attrs, x)
#define hr <hr />
#define p(attrs, x) tag_attr(p, attrs, x)
As you can see, I got bored at one point and didn’t feel like adding any more tags, as they would all have followed the same pattern. We can use it like this:
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "abuse.h"
// clang-format off
#define WATER_CSS "https://cdn.jsdelivr.net/npm/water.css@2/out/water.css"
#define HTML \
head( \
tag(title, C Preprocessor Abuse) \
link(_(rel="stylesheet" href=WATER_CSS)) \
) \
body( \
div(attr(class, "container"), \
h1(, C Preprocessor Abuse) \
hr \
p(, This page was generated entirely by the C preprocessor.) \
p(, HTML tags are defined as function-like macros that expand to \
angle-bracket syntax. The entire page is a single string \
literal assembled at compile time via token stringification.) \
tag(blockquote, tag(em, The compiler is the template engine.)) \
hr \
) \
)
// clang-format on
int main(void) {
char *response = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"\r\n" html(HTML);
int fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
listen(fd, 1);
puts("Listening on http://localhost:8080");
while (true) {
int client = accept(fd, NULL, NULL);
char buf[1024];
read(client, buf, sizeof(buf));
write(client, response, strlen(response));
close(client);
}
}
Here’s the accompanying Makefile:
CC ?= clang
CFLAGS = -std=c23 -Wall -Werror
run: clean abuse
@./abuse
abuse:
@$(CC) $(CFLAGS) -I. -o abuse abuse.c
clean:
@rm -f ./abuse
.PHONY: clean run
The rendered page:

You can find all three files in this gist.
#Summary
This was fun to do and deepened my understanding of the C preprocessor, so it wasn’t entirely pointless. I’m not gonna use this for anything, and you probably shouldn’t either, but I’m not gonna tell you what to do.
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.