是否可以授予顶级函数访问 C 中对象成员的权限?

发布于 2024-11-03 06:44:27 字数 817 浏览 6 评论 0原文

所以我正在为 Win32 中的 GUI 编程编写一些包装类。我从一个 Window 类开始,到目前为止它包含一个 MainLoop 方法,该方法基本上是标准 Win32 WinMain 函数的克隆。这样,我们就可以执行如下操作:

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

在窗口对象的 MainLoop 方法内,它必须通过设置其 lpfnWndProc 成员来创建新的 Win32 窗口。正如任何 Win32 程序员都知道的那样,该成员是一个指向专门定义的 WndProc 函数的函数指针。问题是,如果我要创建一个 WndProc 函数,我需要访问该窗口对象的成员(以便它知道在窗口上绘制什么等)。这给我留下了两个选择(据我所知):

  1. 我可以在顶层定义WndProc,但这会切断对对象成员的访问。

  2. 我可以将其定义为类方法,但它不是 lpfnWndProc 要求的确切函数类型,因此我无法设置它!

谁能帮我解开这个第 22 条军规吗?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

素手挽清风 2024-11-10 06:44:28

您也可以将其设为静态成员函数。 :)
无论如何,解决方案取决于您是否只需要一个窗口或是否需要多个窗口。
首先是单个窗口的解决方案:

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

现在,如果您想允许多个窗口,请使用 std::map (或者如果您的编译器支持 std::unordered_map)。
编辑:此解决方案存在一些微妙的问题。正如 @Ben Voigt 在他的评论中指出的那样,您会遇到先有鸡还是先有蛋的问题,因为 MessageProcCreateWindow(Ex) 内部调用,但仅在 CreateWindow(Ex) 调用您就获得了窗口句柄。这是基于 Ben 的下一条评论的解决方案(谢谢!):

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

不过要小心,因为上面的示例不是线程安全的。您需要互斥锁窗口的创建:

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

以上应该是为了线程安全(我希望)。

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

意中人 2024-11-10 06:44:28

您需要创建窗口地图,当您创建新窗口时,只需将其添加到此全局地图中即可。当然,您可以使用简单的链表来代替。

map<HWND, Window *> wndmap;

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

    ....
}

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

    ....
}
另类 2024-11-10 06:44:28

WndProc 不能是实例成员函数,因为 Windows 不会传递任何隐藏的 this 参数。它可以是命名空间范围或静态成员。

一种简单的解决方案是使用 map 来查找对象,然后将参数转发到该对象的方法。

请注意,WndProc 可以维护地图本身,因为 CreateWindow 提供了一个不透明的用户参数,该参数显示在 WM_CREATE 中,这对于携带 >Window *,然后删除 WM_DESTROY 中的条目。

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.

公布 2024-11-10 06:44:28

将 WndProc 定义为静态类成员 - 这将与非成员函数指针(例如 Win32 编程中使用的指针)兼容(对于我所知道的所有编译器)。

但我不得不说,这有点浪费时间 - 那里有无数的 Windows 类库,而且我不认为世界真的需要另一个。

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.

半夏半凉 2024-11-10 06:44:28

听起来您需要声明该函数而不定义它。这就是原型的用途。

class Object;

void f(Object* o);

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

        void another_method() {
           ...
        }

        ...
};

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

反过来也有可能。

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() {
    ...
}

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() {
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文