Metaprogramming is pretty wide, ranging from primitive textual substitutions like C macros to type-aware hygienic macros ala rust, and from limited scope like C++ templates to full-on program writing like in lisp.
In C and C++ (current versions) the entire module system is built-up around the metaprogramming hack of doing #ifdef header guards. Even this is a bit error prone, but the alternative is only expressible as a compiler intrisic (#pragma once).
In languages like C, Go and early Java, the lack of generics (a type of metaprogramming) makes it impossible to write type-safe generic algorithms forcing casts to void*, interface{} and Object resp.
Implementing type checking for printf-like constructs requires compiler-intrinsics or C++17 constexpr meta-programming.
In C and C++ you must manually implement serialization and deserialization for structs and everything else that is naturally expressed and iterating over the elements of a struct. Alternatively you could use something like protobuf, which (surprise!) has compiles your protobuf file into a C++ program you can include. Using something like Rust's serde is /much/ simpler, and is only possible due to metaprogramming.