Skip to content

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:

  1. Looks up the template and reads the stored copy.
  2. Strips the @register lines and binds the -p<...> values to them, in order.
  3. Finds each ${...} token and resolves it against built-in macros first, then runtime variables, then registered macros.
  4. Applies any transforms, innermost first.
  5. 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:

PlatformLocation
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

CommandWhat 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 -mList templates / macros
cfgen -del -t <Name> / -del -m <name>Delete a template / macro
cfgen -show -trdir / -show -mrdirShow where the registries are stored
cfgen -versionShow the installed version and check GitHub for a newer one
cfgen -hHelp

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 macrosRuntime variables
Set withcfgen -reg -m name value-p<val1,val2> when generating
LastsUntil you delete themOne generation only
Good forAuthor, company, license, emailClass 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

MacroExampleMeaning
${filename}MyClass.hFile name with extension
${stem}MyClassFile name without extension
${ext}.hExtension
${dir}./src/netFolder of the output file
${abspath}/home/user/project/MyClass.hAbsolute path of the output
${cwd}/home/user/projectWhere you ran cfgen from
${date}2026-10-27Today, YYYY-MM-DD
${time}17:37:15Local time
${year}2026Year
${month}OctoberMonth name
${weekday}TuesdayWeekday name
${iso_date}2026-10-27T17:37:15ISO 8601 timestamp
${unix_time}1771330035Unix timestamp

Transforms

Wrap any value as ${transform:${value}}.

TransformInputOutput
upperhello worldHELLO WORLD
lowerHELLO WORLDhello world
titlehello worldHello World
snakeHelloWorldhello_world
camelhello_worldhelloWorld
pascalhello_worldHelloWorld
slugHello World!hello-world
reverseabccba
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 check cfgen -show -m if a value is missing from your output.
  • Template names must be unique. Delete the old one first, or pick another name.
  • snake adds an underscore before every capital and for every space, so Code File Generator becomes code__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.