Question

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...

Was it helpful?

Solution

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.

OTHER TIPS

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));

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top