I've done that, but it can be very error prone and therefore time consuming.
Instead of relying on serialization of existing data structures I actually try to use more general data structures that already exist in a single span of memory.
Then serialization is just a matter of writing them out from start to finish, or allocating them in a memory mapped file.
Can cereal handle user defined types without having to write custom serialization and deserialization functions for each? At first glance this seems to be the main difference.
Yes! Hopefully C++20 or C++25-ish will provide better alternatives for compile-time reflection.
Don't forget that libnop also has NOP_EXTERNAL_STRUCTURE (and friends) to decouple the annotation from the structure definition. This is handy when you have a C ABI with C++ internal implementation. I don't recall seeing a similar facility in other libraries.
Personally, I just write serialization functions using fwrite/fread/write/read, but I’ve used projects which depend on it.
[0] https://capnproto.org/