Casting member function addresses

I'm trying to convert some code to OOP.

I used to have this which worked fine:
1
2
3
4
5
6
7
8
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  wc.lpfnWndProc = WndProc;
  //...
}


But now I have this which gives an error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class cMain
{
  HWND _hwnd;
public:
  HWND CreateMain(HINSTANCE hInstance, int nCmdShow);
  LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

cMain::CreateMain(HINSTANCE hInstance, int nCmdShow)
{
WNDCLASSEX wc;
wc.lpfnWndProc = (WNDPROC)&cMain::WndProc;
//...
}


I now get an error on line 12 above:
1>Main.cpp(35): error C2440: 'type cast' : cannot convert from 'LRESULT (__stdcall cMain::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'
1>          There is no context in which this conversion is possible


In <windows.h>:
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

So I'm trying to convert from:
LPRESULT CALLBACK cMain::* (HWND, UINT,WPARAM,LPARAM) to LPRESULT CALLBACK* (HWND, UINT, WPARAM, LPARAM)

Is there a was to put a member function into such a cast?
If you pause for thought, you'll see that the treatment of a member function differs from non-member functions because member functions need a this pointer. So, they can't be used interchangeably in the way you think.

In your context, the Window ID is the HWND parameter, so you need to map that onto the class instance address (the this pointer) somehow.

Anyway, that's the problem. How you solve it depends on how the rest of the program hangs together.
Thanks, I understand what you're saying but certainly don't know what to do as a solution. I may go back to C for this file for now (until I get a little better at this sort of thing).
This is one way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct window
{
    window( /* .... */ ) : hwnd(0)
    {
         WNDCLASSEX wc;
         wc.lpfnWndProc = &window::window_proc_free_function ;

         // ...

         hwnd = ::CreateWindow( /* ...... */ ) ;
         if( hwnd ) window_map[hwnd] = this ;
         // ...
    }

    ~window()
    {
        window_map.erase(hwnd) ;
    }

    window( const window& ) = delete ;
    window& operator= ( const window& ) = delete ;
    window( window&& ) = delete ;
    window& operator= ( window&& ) = delete ;

    // ...

    virtual LRESULT wnd_proc( UINT u, WPARAM w, LPARAM l )
    {
        LRESULT result = 0 ;

        // ....

        return result ;
    }

    // ...

    private:

        HWND hwnd ;
        // ...
        // ...
        static std::map< HWND, window* > window_map ;

        static LRESULT CALLBACK window_proc_free_function( HWND h, UINT u, WPARAM w, LPARAM l )
        {
            auto iter = window_map.find(h) ;
            if( iter != window_map.end() ) return iter->second->wnd_proc( u, w, l ) ;
            else return ::DefWindowProc( h, u, w, l ) ;
        }
};


That'll do it if you have a C++11 compiler.

You have to delete the Window instance from within the callback on WM_CLOSE.
Whoa ... my mind was just blown.

That just took about 10 minutes to read and unfortunately I don't have a C++11 compiler yet.

I've never seen this syntax before:
1
2
3
4
window( const window& ) = delete ;
window& operator= ( const window& ) = delete ;
window( window&& ) = delete ;
window& operator= ( window&& ) = delete ;

Is it C++11? Does this just disable default copy & assignment constructors by triggering a compiler error if these operations are attempted? If so, would creating private versions of these constructors do the same thing? That's what I am more used to.

I think I can figure out a C++03 version of this easily enough now. I think the auto iter is the only part I need to change (other than the constructors).

/*************************************/

Got it! Here's some working C++03 code. I didn't end up needing to use std::map<>. Making the function static was good enough.

Full code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'  \
                processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <windows.h>

class cMain
{
public:
  cMain( HINSTANCE hInstance, int nCmdShow )
  {
    WNDCLASSEX wc;
    
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = &cMain::WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_ICON1));
    wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_ICON1),IMAGE_ICON,16,16,0);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpszMenuName = NULL; //MAKEINTRESOURCE(IDR_MENU1);
    wc.lpszClassName = "Main Window";

    RegisterClassEx(&wc);

    hwnd = ::CreateWindowEx (  WS_EX_CLIENTEDGE,  "Main Window",  "Window Caption",  WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 340, 410, NULL, NULL, hInstance, NULL);
    
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);    
  }
    
  HWND Handle() { return hwnd; }
private:
  // Inhibit copy/assignments of this class
  cMain( const cMain& ) {}
  cMain& operator= ( const cMain& ) {}
  cMain( cMain&& ) {}
  cMain& operator= ( cMain&& ) {}

  // Members
  HWND hwnd ;

  static LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
  {
    /* STUFF HERE */
    return 0;
  }
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  cMain TheApp(hInstance, nCmdShow);

  MSG Msg;
  while(GetMessage(&Msg, NULL, 0, 0) > 0)
  {
    if (!IsDialogMessage(TheApp.Handle(), &Msg))
    {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
    }
  }
  return Msg.wParam;
}
Last edited on
The example does what I'd do but the C++11 syntax is an unhelpful distraction.

Don't call it a cMain, call it a Window or some related name.

To disable copy, just declare the copy constructor and assignment operator as private without providing an implementation. Just forget the move constructor, there's no C++03 equivalent.

Your example is missing the map that holds the relationship of HWND/this. I suggest you create a private typedef of the map type and use that. To port JLBorges's example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

    private:
        HWND hwnd ;
        // ...
        // ...
        typedef std::map< HWND, window* > window_map_type;
        static window_map_type window_map;

        static LRESULT CALLBACK window_proc_free_function(HWND h, UINT u, WPARAM w, LPARAM l)
        {
            window_map_type::iterator iter = window_map.find(h) ;
            if( iter != window_map.end() ) return iter->second->wnd_proc( u, w, l ) ;
            return ::DefWindowProc( h, u, w, l ) ;
        }
}
Last edited on
> is it C++11? Does this just disable default copy & assignment constructors by triggering a compiler error if these operations are attempted?

Yes.

> If so, would creating private versions of these constructors do the same thing?

In effect, yes. Perhaps you might prefer
1
2
3
4
struct window : private boost::noncopyable 
{
    // ...
};


Defaulted and deleted member functions in C++ can do more:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// A::bar() is only available for int (and types which can be promoted to int)
struct A { void foo() {} void bar(int) {} void bar(double) = delete ; } ;

struct B : A { void foo() = delete ; } ; // B::foo() is not available

struct C : B { C( int v = 6 ) : c(v) {} const int c ; void foo() {} ; } ;


int main()
{
    A a ;
    B b ;
    C c, c2 ;

    a.foo() ; // ok
    a.bar(20) ; // ok
    a.bar( 2.3f ) ; // *** error A::bar(double) is deleted
    b.foo() ; // *** error B::foo is deleted
    b.bar(20) ; // ok, calls A::bar(20)
    c.foo() ; // ok
    c2 = c ; // *** error C::operator= is (implicitly) deleted
}

http://www.stroustrup.com/C++11FAQ.html#default
Topic archived. No new replies allowed.