4

I'm developing a system composed of many independent subsystems. Two of such subsystems are the Window and the GraphicsAdapter subsystem.

The GraphicsAdapter requires a low-level window handle (HWND or X11 Window Handle, depending of the operating system), and the Window subsystem is a way to abstract those OS specific APIs.

If the Window subsystem gave access to low-level API handles, there would be a lot of potential for encapsulation to be broken.

What if it allowed windows to go fullscreen and back, but had to fire events warning the system about those changes, and the low-level handle were used to toggle to fullscreen without its knowledge?

How can I ensure the handle will be safely carried from the Window subsystem to the GraphicsAdapter without being abused, and still be flexible enough to allow other subsystems like the GraphicsAdapter to be added later, while at the same time maintaining type safety?

Is there a way to encapsulate the handles in a way Direct3D and OpenGL could access just enough from the handle to work properly?

-- edit

Additionally to carrying handles safely from one subsystem to another, knowing that subsystems can be written by a different team of coders, for example, is there any way to remind them of the way the handle is meant to be used?

Comments are the obvious choice, but something enforced by the compiler is what I really look for...

Gui Prá
  • 5,559
  • 4
  • 34
  • 59
  • I would encapsulate it in wxWidgets or Qt or GTK or ... – Marcelo Cantos Nov 02 '10 at 08:23
  • 1
    Why would I use wxWidgets, Qt or GTK or whatever fullblown GUI lib out there if all I want is a blank window to render stuff on? Besides, I would still have to access some sort of _nativeHandle() to pick the handle and put on OpenGL/D3D if I wanted to render on those, so it's not what I'm looking for. I want a new solution. – Gui Prá Nov 02 '10 at 08:28
  • What does the GraphicsAdapter need the handle for? And why can't you trust the GraphicsAdapter code? – Cheers and hth. - Alf Nov 02 '10 at 10:23
  • @Alf: he already said: so that he can initialize OpenGL/D3D, which both require handles to the target window. – jalf Nov 02 '10 at 11:22

3 Answers3

6

Both HWNDs and X11 Window Handles are pointer types. You can use this to your advantage. Do something like this:

struct WindowHandleImpl;
typedef WindowHandleImpl *WindowHandle;

C++ allows you to work with pointers to incomplete types just fine - just don't define the contents of WindowHandleImpl anywhere and WindowHandle is a perfectly opaque type that can be passed around by the App without disclosing any implementation details.

Because HWND, X11 Window and WindowHandle are all pointer types, you can cast between them freely and losslessly. So whenever you need to return a wrapped window handle to the app, you do a static_cast<WindowHandle>(some_hwnd), and the same trick works in reverse when you need to convert an app-specified WindowHandle into the actual platform type.

If you ever need to port to a platform that doesn't use a pointer type to denote Window handles, you can wrap that in a struct / class and return a pointer to that.

Fabian Giesen
  • 3,231
  • 16
  • 16
  • I'd probably wrap it in a struct to begin with, just to be safe.. I wouldn't trust Microsoft to *leave* HWNDs as handles. There's probably some obscure `#define` that redefines it to be something else for some reason. And if there isn't, then it's only a matter of time before it is added. ;) +1 though, since you describe both cases very well, and it's a good solution. – jalf Nov 02 '10 at 11:24
  • @jalf: Well, they stuck with pointer types for Win16, Win32 and now 64-bit. This is virtually impossible to change outside of drastic changes like switching to a different platform because it would break binary compatibility, and every single Windows app (except for console ones) depends on the binary interface of user32.dll. If in doubt, keep it simple. If you're really not sure whether you might need to change it later, write two helper functions `WindowHandleFromHWND` and `HWNDFromWindowHandle` and let them do the typecasts. That way, there's only two places to edit if you ever need to. – Fabian Giesen Nov 02 '10 at 18:28
2

Expose the operations on handle through virtual functions:

class GenericHandle
{
    public:
        virtual void some_operation() = 0;
};

// Windows API
class WindowsHandle : public GenericHandle
{
    public:
        virtual void some_operation()
        {
            WndOperation (handle_);
        }
    private:
        HANDLE* handle_;
};

// Some X system API
class XHandle : public GenericHandle
{
    public:
        virtual void some_operation()
        {
           XOperation (handle_);
        }
    private:
        XVOID* handle_;
};

A probable way to configure the GraphicsAdapter:

GraphicsAdapter* ga = new GraphicsAdapter(GenericHandleFactory::get_handle(SYSTEM_ID));
Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93
  • 2
    The problem is OpenGL and Direct3D just won't understand what's a WindowsHandle or XHandle or whatever. It wants either the Windows HWND or the X11 Window Handle. – Gui Prá Nov 02 '10 at 08:26
  • 2
    Actually, to be more precise, OpenGL requires a device context under Windows to create its render context. Direct3D, though, requires the HWND. Both cases are hard to encapsulate, though the D3D case is a lot more dramatic, as usual, since it eats away much more than needed to render (a whole window handle, instead of something representing a place to draw, which is what it really should demand, imo). – Gui Prá Nov 02 '10 at 08:41
  • @n2liquid: there is no way to avoid downcasting in the described situation. Use RTTI on GenericHandle in GraphicsAdapter to get raw data or specific functionality you need. – Basilevs Nov 02 '10 at 09:39
-1

Document the way you expect it to be used.

Trust the other coders on your team.

Don't write a bunch of code trying to straight-jacket your team into coding only the way you want them to. More code means more code to maintain, more chances for bugs, more confusion for the next person looking at this code wondering what the heck it is doing.

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • Documentation is placed where you acquire the HWND. Later on you can pass it away so many times back and forth that nobody will know it should use the way it was documented. It's not a get function anymore: it's a free variable. It shouldn't be like that. OOP fights exactly that sort of philosophy. There should be a way to get them to play along well, the OOP way. If I don't find the way, then certainly I'll just document it. – Gui Prá Nov 05 '10 at 04:02