C++ modules support having templates with private code, compilers than retrieve what they need from the BMI (Binary Module Interface), like in any other module based language that supports binary libraries.
I think what is the right approach is to make sure, that a header file really only contains the "public" API. Common definitions or static/inline functions should be better put into a separate *.c file that is then included in multiple translation units, but since it is separate from the real API, it can be included in much less other files. Some also use the convention to name this files *.inc or *.imp .
As for name conflicts, C has the tooling for this, as compilation and symbol handling are separate steps. It might not be the cleanest approach, but you can always bundle some translation units into a convenience library and apply a linker script, or strip symbols with objcopy.
Symbols names before compilation can be influenced with the preprocessor by naming a macro with the same name, which essentially renames the symbol. For the mentioned conflict of preprocessor names (aka. macros), I see no other way short of modifying the code or undefining/redefining. Most people also use a build system which can trivially call out to sed, before invoking the compiler.
That's if a macro conflicts with another macro, the real fun begins when a macro conflicts with something else in your code, and suddenly a piece of your code gets transparently replaced with something completely different. Granted, in most cases the result won't compile and you'll just spend some time scratching your head and staring at bizarre error messages, but one day you might get especially lucky and it will compile. Good luck debugging that.
It literally is a name conflict. It's not a DEBUGGER SYMBOL type of name conflict but it's still a name collision. A famous example of a macro name conflict is Microsoft windows.h ntdef.h min() max():
https://www.google.com/search?q=Microsoft+windows.h+min+max+...
The "windows.h" is not "incompatible" code. It just has a name conflict that needs a workaround.
It sounds a bit like some people have poor macro hygiene. I thought it's standard to just undefine all macros immediately after use.