After a bit more research and sleep, the factory implementation I used above does seem a bit daft. I've thought about using typedefs, see below.
That is, for small functions like this. On a case by case decision, "inner cluttering" might be preferable. But imho, ideally, a function should be refactored then so it does not have inner cluttering.
So how would you refactor it? Let's use an example a little more complex than a timekeeping function - low-level GUI functionality for Windows, Mac and KDE perhaps - now there's a lot to think about. Somehow we'd need to abstract the creation of a window, keyboard and mouse input, and some basic rendering. Here's something to start off:
class Window
{
public:
Window(const std::string& Title, const Vector2& Dimentions, const Vector2& Position);
virtual ~Window();
void Update(float DeltaTime);
bool HasFocus();
virtual void OnMove() {};
virtual void OnClose() {};
virtual void OnMinimise() {};
virtual void OnMaximise() {};
Vector2 GetMouseAbsCoords();
private:
#if defined _WINDOWS
HWND mWindowHandle;
#elif defined _MACOSX
MacWindow mWindow;
#elif defined _KDE
KDEWindow mWindow;
#endif
bool mHasFocus;
};
Ideally, the user of the library would derive from this class, add their implementation for the OnMove, OnClose callbacks, etc, and it would be their responsibility to call Update periodically, passing in a valid delta. Under the hood, I'd be tempted to have Window.cpp for any generic functionality (HasFocus), and then Window_W32.cpp, Window_KDE.cpp and Window_Mac.cpp containing the constructor, Update and GetMouseCoords bodies.
Now, here's where it gets tricky - what if one platform used a polling mechanism for fetching the current position, and another used an event to set new coords? The latter would mean some extra #ifdefs in the header to store a Vector2 for the current position as and when it changes. Let's say this list of extra #ifdefs grew until the point it gets messy, and we decide to refactor. We'd have a lot of generic variables which are required for all platforms, and each platform implementation required a lot of variables for itself. How about inheritance and typedefs?
class WindowBase
{
public:
Window(const std::string& Title, const Vector2& Dimentions, const Vector2& Position);
virtual ~Window();
virtual void Update(float DeltaTime);
bool HasFocus();
virtual void OnMove();
virtual void OnClose();
virtual void OnMinimise();
virtual void OnMaximise();
virtual Vector2 GetMouseAbsCoords();
private:
//Lots of generic variables for the base implementation
bool mHasFocus:
};
class WindowKDE : public WindowBase
{
public:
WindowKDE(const std::string& Title, const Vector2& Dimentions, const Vector2& Position);
virtual ~WindowKDE();
void Update(float DeltaTime);
Vector2 GetMouseAbsCoords();
private:
//Event-based update callback for this platform
void OnEvent(int EventId, void* EventParams);
//Lots of platform specific variables
KDEWindow mWindow;
};
#if defined _WINDOWS
#include "WindowW32.h"
typedef WindowW32 Window;
#elif defined _MACOSX
#include "WindowMac.h"
typedef WindowMac Window;
#elif defined _LINUX
#include "WindowKDE.h"
typedef WindowKDE Window;
#endif
The usage would remain the same, yet everything would be tidier, the platform #ifdefs are absolutely minimal, and we could make good use of the inheritance hierarchy when calling the Update() function down through each layer.
Your thoughts?
Best of all is perhaps to use boost or Qt (or another cross platform api of your choice) for this.
This is just a learning excercise, wxWidgets is my weapon of choice for anything serious.