C++ Service Starter Pack

An inventory of the frameworks and libraries to use when writing a network service in C++.

IO Framework

Requirements:

If you’re building an IO-intensive, Linux-only service, strongly consider using Seastar. Its thread-per-core architecture is the best for database or load balancer-like services, and provides the ability to upgrade to DPDK/SPDK on bare metal if needed. However, Seastar requires that all potentially blocking calls made in any library must go through Seastar. Thus, it is more of a lifestyle than a framework. This will reduce your choice of potential metrics/logging/etc. library to only what Seastar provides, or you must write your own. If your service contains notable amounts of business logic, or is not critically sensitive to latency, then the advantages of Seastar are unlikely to be worth its costs.

What library to use here is potentially tied to one’s choice of RPC protocol. When using gRPC, it probably makes the most sense to just use grpc/grpcGitHub.

Metrics

Requirements:

Logging

My ideal logging framework permits easy semi-structured logging, while being efficient and flexible. An invocation would look like:

LOG(INFO, "User {user} has performed action {action}.", "admin", "login");

Which will get turned into two things: 1970-00-00 00:00:00 INFO User admin has performed action login. and { "date": "1970-00-00 00:00:00", "severity": "INFO", "user": "admin", "action": login }. The former is for humans to read, the latter is to be uploaded into a data warehouse for aggregation and analysis.

Flags

Requirements:

Some projects define a configuration file which has the extended set of tuning knobs, instead of allowing them to be set as command line flags.

Parsing Documentation Flags vs knobs Runtime setting

gflags

boost.program_options

cxxopts

Legend: ✅ = supported ⛏️ = not supported, but able to be built using the library ❌ = not supported, and the library does not permit this to be built

Testing

The combination of google/googletestGitHub, google/benchmarkGitHub, and google/googlemockGitHub has always worked well enough for me that I’ve never looked elsewhere. They are tools that have not been significantly updated or upgraded for a long time. For a more modern set, consider catchorg/Catch2GitHub, martinus/nanobenchGitHub, and rollbear/trompeloeilGitHub, respectively. "Modern" does not mean uniformly better though.

For randomized testing, you need to answer the question "Can I generate all possible inputs myself?". If yes, use a property testing framework like emil-e/rapidcheckGitHub. If no, then use a coverage driven fuzzer like LibFuzzer.

Setting up sanitizers for both unit and randomized tests will improve coverage. Address Sanitizer and Undefined Behavior Sanitizer should be easiest to set up, as they can be easily applied to a subset of an executable. Thread Sanitizer is very useful when testing multi-threaded code, but causes a notable slowdown. Memory Sanitizer requires all code in the executable to be compiled with Memory Sanitizer, so all libraries used, including libstdc`/`libc. If any of these are too much hassle to set up for the project, then the tests can be run under valgrind instead, but will run notably slower.

Quality of Life

There’s a number of libraries that don’t satisfy a key necessity for a service, but just make development nicer or easier:

Special Purpose

There’s a number of libraries that fall into the category of "If you’re doing a lot of X, then you should use Y."


See discussion of this page on Reddit, Hacker News, and Lobsters.