1

Currently developing a 2-D game-dev environment with a Wrapper/GameEngine class combination in Win32 (C/C++ language). As it stands, I use the Wrapper to set up and initialize all items with the Window as well as to initialize the GameEngine class before entering the message loop.

To do this, I redirect Windows messages sent to WndProc(...) to HandleEvent(...) methods found in both the Wrapper and the GameEngine classes. This is done via static, private shared_ptrs found in the Wrapper class. One such pointer points to the contained GameEngine, and the other points to the Wrapper itself.

An example WndProc function may look like this:

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // Initialize via wrapper, else route to engine
    switch (msg)
    {
        case WM_CREATE:
            return GEN::Wrapper::GetWrapper()->HandleEvent(hWindow, msg, wParam, lParam);
            break;
        default:
            return GEN::Wrapper::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam) // DefWndProc called from this HandleEvent
    }
}

Where GetEngine() and GetWrapper() are static accessor methods that return their respective shared_ptr.

What I'd like to do is incorporate the pImpl idiom in this design. That is, I want to create a wrapper interface class that removes implementation details from the specific wrapper being used. One of the problems ailing me is that I need (or at least think I need) that static accessor method for the Wrapper in question. This is because as I have it each derived Wrapper initializes the window in a game-specific way, and WndProc needs to know where to forward that initial message, as one can see in the code above. Of course, the static method is tied to the wrapper class, so GetWrapper() would be impossible to put into that interface.

Essentially, I want to have the WrapperInterface declared like this:

class WrapperInterface
{
public:
    static std::tr1::shared_ptr<WrapperInterface> // factory function
        create_Wrapper(...); // returns pImpl

    // ... Pure virtuals here                                                               
};

Derive Wrapper publicly from WrapperInterface, and then implement a primitive version of create_Wrapper more or less like this:

std::tr1::shared_ptr<WrapperInterface> WrapperInterface::create_Wrapper(...)
{
    return std::tr1::shared_ptr<WrapperInterface>(new Wrapper(...));
}

so I can put this line in WinMain:

std::tr1::shared_ptr<WrapperInterface>
      Wrapper(WrapperInterface::create(...));

And still have WndProc be able to forward messages to the Interface methods?

Update:

A thought occurred to me to store a static pointer to the WrapperInterface itself and have create_wrapper set that member variable to whatever wrapper the interface is using. I then eliminated Wrapper's static pointer altogether and made the Engine pointer non-static. This somehow feels like cheating, though, as now I'm introducing a private member variable into the interface, albeit a static one. Any thoughts or tips on redesign methods without storing statics would be great!

In any event, thank you all for reading and any advice you may give.

  • 2
    You can associate a pointer to the actual `Wrapper` object with the window that it creates. You can use `SetWindowsLongPtr(GWL_USERDATA)`, `SetProp()`, or `SetWindowSubClass()` for that purpose. Your window procedure can then extract the `Wrapper` object pointer directly from the provided `HWND` (or, in the case of `SetWindowSubClass()`, from the procedure's `dwRefData` parameter) so it does not need to use any global statics to hunt for it. – Remy Lebeau Jul 28 '15 at 00:38
  • My god. What a simple solution. Thank you! Is there a way to upvote the comment? – Joshua Macvey Jul 28 '15 at 00:48
  • You can actually upvote comments, but it would be better to get @RemyLebeau to put his comment in as an answer so you could mark it as accepted – 1800 INFORMATION Jul 28 '15 at 03:58
  • @RemyLebeau Should thank you twice. Did more research and found a few more nuggets you had posted a while back (2011, even) on Win32 API... If you'd like, please put your original comment in as an answer and I'll mark it as accepted! – Joshua Macvey Jul 28 '15 at 07:13

1 Answers1

2

You can associate a pointer to the actual Wrapper object with the window that it creates for itself. To do that, you can either:

  1. use RegisterClass/Ex() with the cbWndExtra field set to sizeof(Wrapper*) to reserve extra memory inside the HWND, then use SetWindowLong/Ptr() with the nIndex parameter set to 0 to store the Wrapper* pointer value inside the allocated memory:

    WNDCLASS wc;
    wc.lpszClassName = TEXT("MyWrapperWindow");
    wc.cbWndExtra = sizeof(Wrapper*);
    ...
    RegisterClass(&wc);
    

    hwnd = CreateWindow(TEXT("MyWrapperWindow"), ...);
    SetWindowLongPtr(hwnd, 0, (LONG_PTR) this);
    
  2. use SetWindowsLong/Ptr() with the nIndex parameter set to GWLP_USERDATA:

    hwnd = CreateWindow(...);
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) this);
    
  3. use SetProp() with a custom name in the lpString parameter:

    static const LPCTSTR szMyProp = TEXT("MyProp");
    
    hwnd = CreateWindow(...);
    SetProp(hwnd, szMyProp, (HANDLE) this);
    
  4. use SetWindowSubclass() with the Wrapper* pointer passed in its dwRefData parameter:

    hwnd = CreateWindow(...);
    SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
    

In cases 1-3, at least (not sure about case 4), you can alternatively pass the Wrapper* pointer in the lpParam parameter of CreateWindow/Ex() and then call one of the mentioned functions inside your window procedure's WM_NCCREATE or WM_CREATE handler:

hwnd = CreateWindow(..., this);

case WM_CREATE:
{
    Wrapper *pThis = (Wrapper*) ((LPCREATESTRUCT)lParam)->lpCreateParams;
    // SetWindowLongPtr(hwnd, 0, (LONG_PTR) pThis);
    // SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pThis);
    // SetProp(hwnd, szMyProp, (HANDLE) pThis);
    break;
}

For all other messages, your window/subclass procedure can extract the Wrapper* pointer when needed. This way, it does not need to use any global statics to hunt for the object:

  1. GetWindowLong/Ptr() with the nIndex parameter set to 0:

    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, 0);
    
  2. GetWindowsLong/Ptr() with the nIndex parameter set to GWLP_USERDATA:

    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    
  3. GetProp(), passing the same lpString pointer that was passed to SetProp() (important!):

    Wrapper *pThis = (Wrapper*) GetProp(hwnd, szMyProp);
    
  4. the subclass procedure's dwRefData parameter:

    Wrapper *pThis = (Wrapper*) dwRefData;
    
IInspectable
  • 46,945
  • 8
  • 85
  • 181
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • `GWLP_USERDATA` belongs to whom ever called `RegisterClassEx` for any particular window class. If that is you, you may wish to sacrifice `sizeof(void*)` bytes of `cbWndExtra` space instead, because someone else may ignore, that `GWLP_USERDATA` technically belongs to you. This is very common. – IInspectable Jul 28 '15 at 15:48
  • The assumption was that the `Wrapper` is creating the HWND internally and not exposing access to it, so it could use `GWL_USERDATA` for itself. But I have updated my answer to include your suggestion. – Remy Lebeau Jul 28 '15 at 17:19
  • @RemyLebeau Wrapper is indeed creating the HWND internally. Idea was to keep most if not all the window initialization code separated from the game code. I ended up using the alternative method mentioned above (setting the `lParam` in `CreateWindow(...)` to, as you said, avoid statics (all except the Create_Wrapper static function in the interface, which is stored in a library). Thanks again! – Joshua Macvey Jul 28 '15 at 18:24