This post isn’t very advanced, just a reminder about basics that help our lives.
The value you can squeeze out of the C and C++ type systems (for all their deficiencies) is massive.
Consider a asset loading API where there are multiple clients spread around a big engine. The goal is to allow code to request some asset data to be streamed in. Assume the asset data can be split in a few different distinct types as well, to support things like texture streaming (to split base mips away from high mips and keep two distinct data blocks around.)
One common approach is this API at the lowest level:
bool BeginAssetLoad(const char* path);
You’ll then maybe expose a couple of helper functions to make paths and assorted helpers.
But a better API is:
bool BeginAssetLoad(AssetId asset_id, AssetSubType sub_type);
Here are a few reasons why I think this style of API is better:
- If we decide to change how we represent asset ids or subtypes, we get compile errors for every single call site and we can fix them up statically.
- If we add a new required parameter, we again get compile errors and see the scope of the change.
- If we change our minds on how assets are represented on the file system/archive side, we have a single point of change as no one but this low-level function actually deals with the translation from asset ids to filenames or archive hashes, or whatever loading approach we use.
- We avoid questions of lifetime for the passed in character string.
- Client code can’t easily pass in invalid data.
- If our AssetId type is based on a simple numerical type, we can use conditional breakpoints to break when a particular asset is loaded.
Some very nice advantages. The basic idea is to retain type information for as long as possible through the data flow, and transform to a less precise form as late as possible. It’s easy to say, but sometimes hard to enforce!