🖥️ CLI11 — Command line parsing for C++ without the pain
Every server tool, every daemon, every diagnostic utility needs command line arguments. And every C++ developer has at some point wrestled with getopt, manually parsed argv, or pulled in Boost just for program_options.
There’s a better way. 🎯
📦 Header-only & C++11
Drop in a single header, done. No build system changes, no extra dependencies, works on any compiler supporting C++11 and up. Perfect for embedded Linux targets and minimal server builds where you control the toolchain.
⚙️ The basics are refreshingly clean:
app.add_option("-p,--port", port, "Port to listen on");
app.add_flag("-v,--verbose", verbose, "Enable verbose output");
CLI11_PARSE(app, argc, argv);
That’s it. Types, defaults, descriptions — all in one line per argument.
🌳 Subcommands — for tools that grow
When your binary does more than one thing, CLI11 handles it naturally:
auto start = app.add_subcommand("start", "Start the server");
auto stop = app.add_subcommand("stop", "Stop the server");
Same pattern as git, docker, systemctl. Your users already know how it works.
🛡️ And because it’s CLI11, you get automatic –help, type validation, and useful error messages for free — without writing a single line of parsing logic yourself.
🐧 Less boilerplate. More server.
🔍 Optimizing base64 decoding in jwt-cpp
jwt-cpp is a popular header-only C++ library for creating and verifying JSON Web Tokens. Clean, lightweight, no heavy dependencies — exactly the kind of lib you want in a Linux server stack.
While working with it I noticed a performance issue in the base64 decoding path. 🐛
⚙️ The old approach:
For every character in the input, the decoder called std::find_if to search linearly through a 64-char alphabet array.
→ O(n) per character lookup
→ Called for every single byte being decoded
→ A JWT with a large payload = a lot of unnecessary searching
🔧 The fix — a reverse lookup table:
Instead of searching, I precomputed a 256-entry lookup table. Each array index represents a byte value, each entry its base64 value — or -1 if invalid.
Decoding a character becomes a single array access:
auto index = rdata[static_cast<unsigned char>(symbol)];
→ O(1) per character lookup
→ No iteration, no comparisons
→ constexpr — lives in read-only memory, CPU cache friendly
📊 For JWT verification on a busy server this happens thousands of times per second. Small change, real impact.
👉 github.com/Thalhammer/jwt-cpp/commit/59cdb43
🛑 Graceful Shutdown in C++ — because kill -9 is not a deployment strategy
Your server handles thousands of connections. You deploy a new version. What happens to the clients mid-request when the process dies?
Without graceful shutdown — nothing good. 💀
🔌 Step 1 — Catch the signal
The OS sends SIGTERM before it sends SIGKILL. That’s your window.
signal(SIGTERM, on_shutdown);
Set a flag, don’t do heavy work inside the handler. Simple and safe.
🚫 Step 2 — Stop accepting new connections
Close or stop listening on your socket immediately. New clients will get a clean refusal instead of a sudden reset.
⏳ Step 3 — Drain existing connections
Let active requests finish. Give them a deadline — 5 or 10 seconds is usually enough. After that, you cut them loose. Your call.
📝 Step 4 — Flush your logs
Before the process exits, tell spdlog to flush:
—> spdlog::shutdown();
Because the last thing you want is missing log entries right before a crash or restart.
🔄 Combined with SO_REUSEPORT the full flow becomes elegant:
→ New process starts, binds the port
→ Old process catches SIGTERM
→ Drains connections, flushes logs
→ Exits cleanly
Zero downtime. Zero lost requests. Zero mystery gaps in your logs. 🐧
Subscribe to #cpp entries via RSS feed