Preprocessor isn't a bad thing in and of itself. It's a powerful tool, just like many other development tools. The question is how do you use it?
If you make your preprocessing clear, documented, and logical, it's not a problem at all. It's when you start doing "clever" tricks that are difficult to decipher that it becomes a problem.
Ideally, you would limit how often you do this. So you would perhaps have a defs.h
that you include in all your files, and inside that one file you do your OS specific logic.