Skip to content

Instantly share code, notes, and snippets.

@dhilst
Created April 16, 2025 14:11
Show Gist options
  • Save dhilst/1a63ad17664b771001b3eb9068106891 to your computer and use it in GitHub Desktop.
Save dhilst/1a63ad17664b771001b3eb9068106891 to your computer and use it in GitHub Desktop.
Type safe primitives in C++
// Phantom is a phatom type. It means that it is not used
// during runtime, but play type safety roles during compile
// time.
template <typename Phantom>
struct StrongId {
int id;
// Conversion int -> StrongId<T> must to be explicit
explicit StrongId(auto id_) : id(id_) {}
// Conversion StrongId<T> -> int is implicit
constexpr operator auto() const { return id; }
};
// We can use the type being defined to define strong primitives
// like int. In fact we can generalize StrongId to accept another
// type parameter and use any time instead of 'int', but I'm not
// doing this here for sake of simplicity.
//
// Here we use the struct being defined to define strongly typed
// ids. These ids CANNOT be swapped by accident because the program
// is ill-typed if you do so.
struct Store { StrongId<Store> id; };
struct Product { StrongId<Product> id; };
// Even if 'Product' is an incomplete type
// inside its own definition, this does not matter much because the
// type is not used at runtime, so its size does not matter in this case.
// Note: Incomplete type is a struct or class that is declared but not
// defined, for example `struct foo;` declares a struct but does not
// defines it. Because is not defined its size is unknown, and because of
// that there are things that you cannot do with it, like allocating it,
// (how much bytes would you allocate?), but all this is off-topic
// Back to phantom types, this just an example function accepting an int
void findById(int id) {}
// This function cannot be called with a StrongId<Store> by accident
void findProductById(StrongId<Product> productId) {
// operator int() converts it to int automatically
findById(productId);
}
void testStrongId(Product product, Store store) {
// You can't pass a simple int to it, it need to be wrapped
findProductById(1); // Type error: No matching function for call to 'findProductById'
findProductById(product.id); // ok
findProductById(store.id); // Type error: No matching function for call to 'findProductById'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment