Tool
CFGen
A single-binary CLI that generates files from registered templates, with macros, runtime variables, chainable text transforms and built-in dates and filenames.
Every new C++ header or Python module starts with the same block of metadata:
author, company, license, date, filename. I kept pasting it from the last file
and forgetting to change something. CFGen removes that step. You register a
file as a template, and each time you generate from it, the values are filled in
for you.
It is a single binary written in C++20, with no runtime and no dependencies. Current version: 2.0.0.
Why not something else
IDE snippets are locked to one editor. Shell scripts work, but you end up
writing a new one per template. Cookiecutter and Yeoman need a Python or Node
setup and a config format just to produce one header. CFGen sits in between: a
binary on your PATH, templates stored once, and one command to use them.
A quick example
Register the values that never change, once:
cfgen -reg -m author "Ada Lovelace"
cfgen -reg -m company "Babbage & Co."
cfgen -reg -m license "MIT"
Write a template, for example cpp_header.h:
// File : ${filename}
// Author : ${author}
// License : ${license}
// Created : ${date}
@register ClassName
@register ProjectName
#ifndef ${upper:${snake:${ClassName}}}_H
#define ${upper:${snake:${ClassName}}}_H
namespace ${snake:${ProjectName}}
{
// ...
}
#endif // ${upper:${snake:${ClassName}}}_H
Register it and generate files from it:
cfgen -reg -t CppHeader cpp_header.h
cfgen -gen src/TcpSocket.h CppHeader "-p<TcpSocket,SocketLib>"
cfgen -gen src/UdpSocket.h CppHeader "-p<UdpSocket,SocketLib>"
The first command gives a file starting like this:
// File : TcpSocket.h
// Author : Ada Lovelace
// License : MIT
// Created : 2026-10-27
#ifndef TCP_SOCKET_H
#define TCP_SOCKET_H
namespace socket_lib
{
The author and license came from the registry. The filename and date were
resolved by CFGen. The class and project names came from -p<...>.
How it works
CFGen keeps two registries: templates (a name pointing to a stored copy of the
file) and macros (a name pointing to a value). When you run -gen, it:
- Looks up the template and reads the stored copy.
- Strips the
@registerlines and binds the-p<...>values to them, in order. - Finds each
${...}token and resolves it against built-in macros first, then runtime variables, then registered macros. - Applies any transforms, innermost first.
- Writes the result to the output path, creating the folder if needed.
Registering a template copies it, so you can move or delete the original file afterwards. Both registries live in your app data folder:
| Platform | Location |
|---|---|
| Windows | %APPDATA%\CFGen\ |
| Linux | ~/.config/CFGen/ |
| macOS | ~/Library/Application Support/CFGen/ |
The stored .map files are plain text, so you can open and read them.
Commands
| Command | What it does |
|---|---|
cfgen -reg -t <Name> <file> | Register a file as a template |
cfgen -reg -m <name> <value> | Register a macro (quote values with spaces) |
cfgen -gen <output> <Name> | Generate a file from a template |
cfgen -gen <output> <Name> "-p<a,b>" | Generate with runtime variables |
cfgen -show -t / -show -m | List templates / macros |
cfgen -del -t <Name> / -del -m <name> | Delete a template / macro |
cfgen -show -trdir / -show -mrdir | Show where the registries are stored |
cfgen -version | Show the installed version and check GitHub for a newer one |
cfgen -h | Help |
The version check only runs when you ask for it. I did not want a network call slowing down every generate.
Macros and runtime variables
Both fill ${...} slots, but they suit different values.
| Registered macros | Runtime variables | |
|---|---|---|
| Set with | cfgen -reg -m name value | -p<val1,val2> when generating |
| Lasts | Until you delete them | One generation only |
| Good for | Author, company, license, email | Class name, project name, module name |
If a template declares two @register lines and you pass one value, CFGen
stops with an error and shows which variable is missing. If you pass too many,
the extras are ignored and it warns you.
Built-in macros
| Macro | Example | Meaning |
|---|---|---|
${filename} | MyClass.h | File name with extension |
${stem} | MyClass | File name without extension |
${ext} | .h | Extension |
${dir} | ./src/net | Folder of the output file |
${abspath} | /home/user/project/MyClass.h | Absolute path of the output |
${cwd} | /home/user/project | Where you ran cfgen from |
${date} | 2026-10-27 | Today, YYYY-MM-DD |
${time} | 17:37:15 | Local time |
${year} | 2026 | Year |
${month} | October | Month name |
${weekday} | Tuesday | Weekday name |
${iso_date} | 2026-10-27T17:37:15 | ISO 8601 timestamp |
${unix_time} | 1771330035 | Unix timestamp |
Transforms
Wrap any value as ${transform:${value}}.
| Transform | Input | Output |
|---|---|---|
upper | hello world | HELLO WORLD |
lower | HELLO WORLD | hello world |
title | hello world | Hello World |
snake | HelloWorld | hello_world |
camel | hello_world | helloWorld |
pascal | hello_world | HelloWorld |
slug | Hello World! | hello-world |
reverse | abc | cba |
trim | padded | padded |
They nest, and the inner one runs first. ${upper:${snake:${ClassName}}} turns
PressureTest into pressure_test and then PRESSURE_TEST. The inner part
must be a full token: ${upper:${stem}} works, ${upper:stem} does not.
Install
Download the binary for your platform from the releases page, rename it to
cfgen (cfgen.exe on Windows) and put it on your PATH. On Linux and macOS,
run chmod +x cfgen first.
On macOS, Gatekeeper may block a downloaded binary. If you see
operation not permitted, run xattr -d com.apple.quarantine ./cfgen once.
To build from source you need a C++20 compiler (GCC 10+ or Clang 12+). Each
platform has a script in the repo’s scripts/ folder.
Good to know
- An unknown
${something}produces a warning and is replaced with nothing, so checkcfgen -show -mif a value is missing from your output. - Template names must be unique. Delete the old one first, or pick another name.
snakeadds an underscore before every capital and for every space, soCode File Generatorbecomescode__file__generator. Store space-separated values in lowercase if you plan to snake them.- Unbalanced
${or}in a template is reported as an error. - No conditionals, loops or prompts inside templates, and it generates one file at a time, not whole project trees. If you need those, Cookiecutter or a full template language fits better.