If they all share the same interface, why do you even need to switch among them? Here's some better ideas.
First one: have the preprocessor magic in one place. Put this in a header:
#if defined(PLATFORM_ONE)
#include "platform_one/implementation.hpp"
typedef PlatformOneImplementation TheClass;
#elif defined(PLATFORM_TWO)
#include "platform_two/implementation.hpp"
typedef PlatformTwoImplementation TheClass;
#else
#error No implementation available.
#endif
This would be the only place in your project that uses the preprocessor. Everything else just uses TheClass and nothing platform-dependent. If it's dependent, then the implementation class should hide it.
And as mentioned above, use project configurations to switch.
Here's an even better option. Put this in a header:
class TheClass {
public:
TheClass();
~TheClass();
// Public stuff goes here.
private:
struct Impl;
std::unique_ptr<Impl> pimpl;
};
Then you have one .cpp file per platform and use project configurations to only compile the right one:
// platform_one/implementation.cpp
struct TheClass::Impl {
PlatformSpecificStuff stuff;
MoreStuff more;
};
TheClass::TheClass() : pimpl(new Impl) {}
TheClass::~TheClass() {}
// Implementation of public functions here.
Both of these options are infinitely better than littering your code with preprocessor switches.