Template , Inheritance and the good ol Linker

I have the following piece of code, which resolves in 2 linker errors
Dialog_h wrote:
1
2
3
4
5
6
7
8
9
template<class DT>
class CDialog
{
private:
	CDialog() {};
public:
	CDialog(CClient*, const std::string&);
	virtual ~CDialog<DT>(void);
[...]


Dialog_cpp wrote:
1
2
3
4
5
6
7
8
9
10
template<class DT>
CDialog<DT>::CDialog(CClient* pClient, const std::string& strTargetUsername) : m_pClient(pClient)
{
	CreateThisWindow();
}

template<class DT>
CDialog<DT>::~CDialog(void)
{
}


1
2
3
4
5
6
7
8
9
10
11
12
#include "dialog.h"

class CChatDialog :
	public CDialog<CChatDialog>
{
public:
	CChatDialog(void);
	virtual ~CChatDialog(void);

private:
	virtual INT_PTR DLGPROC(HWND, UINT, WPARAM, LPARAM);
};

1
2
3
4
5
6
7
CChatDialog::CChatDialog(void) : CDialog(NULL, "User")
{
}

CChatDialog::~CChatDialog(void)
{
}


This resolves, as You might have expected in 2 linker errors (unresolved external symbols)...


I did "researching" and found the thing with including the member functions in the header...
So i did obey... But there is still one linker error (unresolved external symbol) left to lose

I can´t find the reason:/





EDIT: The linker cannot find/resolve the friend function _CreateThisWindow which is called by CreateThisWindow()

1
2
3
4
template<class DT>
INT_PTR CALLBACK CDialog<DT>::_DLGPROC(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	DT *pThis;		//Used to buffer the Dialog 


I think it has to do with the fact, that i got a templated function here, tpo. But it´s DT has nothin to do with the DT from the class Dialog

Would it help if I specialize it?... (template<CChatDialog>)

If I do insert the template<class DT> before the friend declaration in the Dialog-class the linker error vanquishes, but :
Error 1 error C2664: 'CreateThread' : cannot convert parameter 3 from 'DWORD (__stdcall *)(void *)' to 'LPTHREAD_START_ROUTINE' c:\users\incubbus\documents\visual studio 2010\projects\client\client\dialog.h 72
Last edited on
Any suggestions?... Im totally stuck on this...
Don't forget that the 3rd param for CreateThread should be declared like this:

DWORD WINAPI ThreadFunction( LPVOID lpParam ); //if you omit WINAPI it won't work!

Also, if it's a member function of some class, you should make it static (and perhaps public).
Last edited on
This is the Thread Proc...

The problem is, that the template stuff ruings the prototype for CreateThread...

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
template<class DT>
DWORD WINAPI _CreateThisWindow(void* v)
{
	//Creation
	DT *pWindow = (DT*)v;

	//Create Window
	pWindow->m_hWnd = CreateDialogParam(NULL, MAKEINTRESOURCE(pWindow->m_lIDD), NULL, pWindow->DLGPROC,(LPARAM)pWindow);
	if(pWindow->m_hWnd == NULL)
	{
		MessageBox(NULL, "Could Not Create Window", "ERROR", MB_ICONEXCLAMATION);
		return 0;
	}

	//Show Window
	ShowWindow(pWindow->m_hWnd, SW_SHOW);

	//Message Loop
	MSG msg;
	while(GetMessage(&msg, NULL, 0,0) != 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}
Mmm... What are the possible types for the template parameter, DT? Are they some classes derived from a base dialog class you made? If so, have you thought of doing the whole thing using polymorphism (base class pointers, virtual functions etc...) instead of templates? Are you sure that it's the template thing that ruins it? Did you try to remove it and see if it generates the same error?

EDIT: Yeah, I tried some sample code myself and it indeed is the template thing that ruins it...
Last edited on
Ok, I think I've found a way to do it! :D Take a look here, it has an example of multithreading if you scroll down a bit -> http://msdn.microsoft.com/en-us/library/ms682453%28VS.85%29.aspx
I took this example and modified it a bit to support templates:

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
67
68
69
70
71
72
73
#include <windows.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>

template <class T> //<- I added this here
DWORD WINAPI ThreadProc (LPVOID lpdwThreadParam );

//Global variable Shared by all threads
int nGlobalCount = 0;
//Main function which starts out each thread
int __cdecl main( int argc, char **argv)
{
int i, nThreads = 5;
DWORD dwThreadId;
//Determine the number of threads to start
if (argc > 1) {
nThreads = atoi( argv[1]);
}

//Set the global count to number of threads
nGlobalCount = nThreads;
//Start the threads
for (i=1; i<= nThreads; i++) {
//printf("i - %d\n",i);
if (CreateThread(NULL, //Choose default security
0, //Default stack size

&ThreadProc<int>,
//CAREFUL HERE, you must say
//which ThreadProc<T> you want!
//Try making it a double and
//see the difference.

//Routine to execute
(LPVOID) &i, //Thread parameter
0, //Immediately run the thread
&dwThreadId //Thread Id
) == NULL)
{
printf("Error Creating Thread#: %d\n",i);
return(1);
}
else
{
printf("Global Thread Count: %d %d %d\n", nGlobalCount, nThreads, i);
Sleep(1000);
}
}
return 0;
}

//Thread Routine
template <class T> //<- I added this here
DWORD WINAPI ThreadProc (LPVOID lpdwThreadParam )
{

//*****************************
//template stuff
//*****************************
    T asdf(1.5);
    cout << "asdf: " << asdf << endl;
//*****************************
//enough of template stuff...
//*****************************

//Print Thread Number
printf ("Thread #: %d\n", *((int*)lpdwThreadParam));
//Reduce the count
nGlobalCount--;
//ENd of thread
return 0;
}

But if you want to be able to decide on run-time what type of window will be created, I think you'll have to resort to polymorphism. Templates can't help much here...
Last edited on
Hey... Thank You for your interest and your help :)... I did try to put the specifier in front of the function´s name instead on the back ^^...
I am wondering what type i should put into the template brackets, when creating an base class pointer...


1
2
3
4
5
6
7
8
DWORD WINAPI _CreateThisWindow(void* v)
{
	//Creation
	CDialog</*What in here???*/> *pWindow = (CDialog</*Same Here*/> *)v;

	//Create Window
	pWindow->m_hWnd = CreateDialogParam(NULL, MAKEINTRESOURCE(pWindow->m_lIDD), NULL, pWindow->DLGPROC,(LPARAM)pWindow);
[...]
Last edited on
I'll ask you again. Why do you use templates to do this? Wouldn't it be easier if you used inheritance?

1
2
3
4
5
6
7
8
9
class Dialog
{
public:
    Dialog();
    virtual ~Dialog();
    //...
private:
    //...
};

1
2
3
4
5
6
7
8
9
class ChatDialog: public Dialog
{
public:
    ChatDialog();
    virtual ~ChatDialog();
    //...
private:
    //...
};

etc...

Why do you want to use templates? What are you trying to achieve?
Last edited on
Ahhh... Sorry, missed that...

I am using inheritance to get the actual Dialogs I want to have, but, I want to make the Message Handler be written specific to each indivual Dialog´s needs...

I got it from: http://msdn.microsoft.com/en-us/library/ff381400%28v=VS.85%29.aspx (the object oriented approach)
Ok... changed to the inheritance style (even if - what I done - may be totally wrong^^)...

When i create one of the windows, i get an stack overlow O_o... Do You find it?...

The Dialog is not shown, and I don´t receive a handle to the window but the message pipe processes and processes... and then the stack overflow appears :/...

ChatDialog.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "ChatDialog.h"

CChatDialog::CChatDialog(void) : CDialog(NULL, "User")
{
}

INT_PTR CChatDialog::DLGPROC(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return true;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		return true;
	default:
		return DefDlgProc(hWnd, uMsg, wParam, lParam);
	}
}


Dialog.cpp
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
#include "Dialog.h"
#include "IncStandard.h"
#include "IncStatus.h"
#include "Client.h"
#include "resource.h"

CDialog::CDialog(CClient* pClient, const std::string& strTargetUsername) : m_pClient(pClient)
{
	CreateThisWindow();
}

void CDialog::CreateThisWindow()
{
	//Create Message Thread
	CreateThread(NULL, 0, _CreateThisWindow, (void*)this, 0, NULL);
}

INT_PTR CALLBACK CDialog::_DLGPROC(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	CDialog *pThis;		//Used to buffer the Dialog

	if(uMsg == WM_INITDIALOG)
	{
		pThis = (CDialog*)lParam;
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
		pThis->m_hWnd = hWnd;
	}
	else
	{
		pThis = (CDialog*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	}

	if(pThis)
		return pThis->DLGPROC(hWnd, uMsg, wParam, lParam);
	else
		return DefDlgProc(hWnd, uMsg, wParam, lParam);
}

DWORD WINAPI _CreateThisWindow(void* v)
{
	//Creation
	CDialog *pWindow = (CDialog*)v;

	//Create Window
	pWindow->m_hWnd = CreateDialogParam(NULL, MAKEINTRESOURCE(IDD_CHATDIALOG), NULL, CDialog::_DLGPROC,(LPARAM)pWindow);		//DLG Template IDD_CHATDIALOG for testing issue
	if(pWindow->m_hWnd == NULL)
	{
		MessageBox(NULL, "Could Not Create Window", "ERROR", MB_ICONEXCLAMATION);
		return 0;
	}

	//Show Window
	ShowWindow(pWindow->m_hWnd, SW_SHOW);

	//Message Loop
	MSG msg;
	while(GetMessage(&msg, NULL, 0,0) != 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}
EDIT: Got it... I forgot, that You should never call DefDlgProc to handle unhandled messages...
Just return false, and the Dialog Manager does it for You...
Last edited on
Topic archived. No new replies allowed.