开发者

Is it possible to give a top-level function access to an object's members in C++?

开发者 https://www.devze.com 2023-02-28 09:25 出处:网络
So I\'m writing some wrapper classes for GUI programming in Win32. I\'m starting with a Window class, and so far it contains a MainLoop method that is basically a clone of the standard Win32 WinMain f

So I'm writing some wrapper classes for GUI programming in Win32. I'm starting with a Window class, and so far it contains a MainLoop method that is basically a clone of the standard Win32 WinMain function. That way, one can do something like this:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) {
  Window *win = new Window();

  // Do all your widget creation and add it to the window object...

  return win->MainLoop(hInst, hPrev, szCmdLine, nCmdShow);
}

Inside the window object's MainLoop method, it must create the new Win32 window by setting its lpfnWndProc member. This member, as any Win32 programmer knows, is a function pointer to a specifically defined WndProc function. The problem is, if I were to create a WndProc function, I would need access to that window object's members (so that it knew what to draw on the window, etc.). This leaves me two options (that I know of):

  1. I can define WndProc at the top level, but that cuts off access to the object's members.

  2. I开发者_如何学Go can define it as a class method, but then it's not the exact function type that lpfnWndProc asks for, so I can't set it!

Can anyone help me unravel this catch-22?


You could also make it a static member function. :)
Anyways, a solution depends on if you need only one window or if you need multiple windows. First a solution for single windows:

// in .h
class Window{
public:
  static LRESULT WINAPI MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  LRESULT InternalMessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  // ...
};

// in .cpp
#include "Window.h"

Window* global_window = 0;

Window::Window(/*...*/){
  if(!global_window)
    global_window = this;
  else
    // error or exception... or something else
}

LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  return global_window->InternalMessageProc(hWnd, msg, wParam, lParam);
}

Now if you want to allow multiple windows, use a std::map (or if your compiler supports std::unordered_map).
Edit: This solution comes with some subtle problems. As @Ben Voigt points out in his comment, you get a chicken and egg problem as the MessageProc is called inside of CreateWindow(Ex), but only after the CreateWindow(Ex) call you have the window handle. Here's a solution based on Ben's next comment (thanks!):

// Window.h stays the same

// in .cpp
#include "Window.h"
#include <map>

std::map<HWND, Window*> window_map;
Window* currently_created_window = 0;

Window::Window(){
  currently_created_window = this;
  window_handle = CreateWindow(/*...*/);
}

LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
  // if the key 'hWnd' doesn't exist yet in the map
  // a new key-value pair gets created and the value gets value-initialized
  // which, in case of a pointer, is 0
  if(window_map[hWnd] == 0){
    // window doesn't exist yet in the map, add it
    window_map[hWnd] = currently_created_window;
  }
  window_map[hWnd]->InternalMessageProc(hWnd, msg, wParam, lParam);
}

Be cautious though, as the above example isn't thread-safe. You need to mutex-lock the creation of the window:

Window::Window(/*...*/){
  Lock lock_it(your_mutex);
  currently_created_window = this;
  window_handle = CreateWindow(/*...*/);
  lock_it.release();
  // rest of the initialization
}

The above should do for the thread-safety (I hope).


You need to create window map and when you create new window just add it to this global map. You can use simple linked list instead of course.

map<HWND, Window *> wndmap;

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
    Window *pWnd = wndmap [hwnd];

    ....
}


WndProc cannot be an instance member function, because Windows will not pass any hidden this parameter. It can be namespace scope or a static member.

One simple solution is to use a map<HWND, Window*> to find the object, and then forward parameters to a method on the object.

Note that WndProc can maintain the map itself, since CreateWindow provides an opaque user parameter that shows up in WM_CREATE which is useful for carrying the Window *, and then you remove the entry in WM_DESTROY.


Define your WndProc as a static class member - this will then be compatible (for all compilers I'm aware of) with non-member function pointer, such as those used in Win32 programming.

But I have to say that this is a bit of a waste of time - there are a zillion Windows class libraries out there, and I don't think the world really needs another one.


Sounds you need to declare the function without defining it. That's what prototypes are for.

class Object;

void f(Object* o);

class Object {
    public:
        ...
        void some_method() {
           ... &f ...
        }

        void another_method() {
           ...
        }

        ...
};

void f(Object* o) {
    ...
    o->another_method();
    ...
}

The other way around might also be possible.

class Object {
    public:
        ...
        void some_method();
        void another_method();
        ...
};

void f(Object* o) {
    ...
    o->another_method();
    ...
}

void Object::some_method() {
    ... &f ...
}

void Object::another_method() {
    ...
}
0

精彩评论

暂无评论...
验证码 换一张
取 消