Monday, December 21, 2009

How to do the definition of a template class in .h file and implementation of the template classes in .cpp file?

The common procedure in C++ is to put class definition in a C++ header file and the implementation in a C++ source file. Then the source file is made part of the project, meaning compiled separately. But when we implement this procedure for template class, some compilation and linking problem will arise.

1. Compilation

PROBLEM

Sample Code:

Template Class Header File

// TestTemp.h

#ifndef _TESTTEMP_H_

#define _TESTTEMP_H_

template

class TestTemp

{

public:

TestTemp();

void SetValue( T obj_i );

T Getalue();

private:

T m_Obj;

};

#endif

Template Class Source File

// TestTemp.cpp

#include "TestTemp.h"

TestTemp::TestTemp()

{

}

void TestTemp::SetValue( T obj_i )

{

}

T TestTemp::Getalue()

{

return m_Obj;

}

If you try to implement the template class like a normal class implementation as shown above, it will generate a set of compilation errors such as

: error C2955: 'TestTemp' : use of class template requires template argument list

: error C2065: 'T' : undeclared identifier

REASON

In this case, the compiler doesn’t know about the object type. So it will not compile.

SOLUTION

To compile this class without any errors you need to put the template specific declaration in .cpp file also as shown below.

Sample Code:

Template Class Header File

// TestTemp.h

#ifndef _TESTTEMP_H_

#define _TESTTEMP_H_

template

class TestTemp

{

public:

TestTemp();

void SetValue( T obj_i );

T Getalue();

private:

T m_Obj;

};

#endif

Template Class Source File

// TestTemp.cpp

#include "TestTemp.h"

template

TestTemp::TestTemp()

{

}

template

void TestTemp::SetValue( T obj_i )

{

}

template

T TestTemp::Getalue()

{

return m_Obj;

}

2. Linking

PROBLEM

With the above code, after resolving all the compilation error you may get some link errors while you create an object of this class in any file other than the TestTemp.cpp file.

Sample Code:

Client Source File

// Client.cpp

#include "TestTemp.h"

:

TestTemp TempObj;

:

: error LNK2001: unresolved external symbol "public: __thiscall TestTemp::TestTemp(void)" (??0?$TestTemp@H@@QAE@XZ)

REASON

When the compiler does encounter a declaration of a TestTemp object of some specific type, e.g., int, it must have access to the template implementation source. Otherwise, it will have no idea how to construct the TestTemp member functions. And if you have put the implementation in a source (TestTemp.cpp) file and made it a separate part of the project, the compiler will not be able to find it when it is trying to compile the client source file. And #including the header file (TestTemp.h) will not sufficient at that time. That only tells the compiler how to allocate for the object data, and how to build the calls to the member functions, not how to build the member functions. And again, the compiler won’t complain. It will assume that these functions are provided elsewhere, and leave it to the linker to find them. So, when it’s time to link you will get "unresolved references" to any of the Class member functions that are not defined "inline" in the class definition.

SOULTION

There are different methods to solve this problem. You can select from any of these methods which is suitable for your application design.

METHOD1:

You can create the object of template class in the same file source file where it is implemented (TestTemp.cpp). So that there is no need to link the object creation code with its actual implementation in some other file. This will cause the compiler to compile these particular types, so the associated class member functions will be available at link time.

Sample Code:

Template Class Header File

// TestTemp.h

#ifndef _TESTTEMP_H_

#define _TESTTEMP_H_

template

class TestTemp

{

public:

TestTemp();

void SetValue( T obj_i );

T Getalue();

private:

T m_Obj;

};

#endif

Template Class Source File

// TestTemp.cpp

#include "TestTemp.h"

template

TestTemp::TestTemp()

{

}

template

void TestTemp::SetValue( T obj_i )

{

}

template

T TestTemp::Getalue()

{

return m_Obj;

}

void TemporaryFunction ()

{

TestTemp TempObj;

}

Client Source File

// Client.cpp

#include "TestTemp.h"

:

TestTemp TempObj;

TempObj.SetValue( 2 );

int nValue = TempObj.Getalue();

:

No need to call this TemporaryFunction() function, it’s just to avoid link error.

METHODE 2

You can #include the source file that implements your template class in your client source file.

Sample Code:

Template Class Header File

// TestTemp.h

#ifndef _TESTTEMP_H_

#define _TESTTEMP_H_

template

class TestTemp

{

public:

TestTemp();

void SetValue( T obj_i );

T Getalue();

private:

T m_Obj;

};

#endif

Template Class Source File

// TestTemp.cpp

#include "TestTemp.h"

template

TestTemp::TestTemp()

{

}

template

void TestTemp::SetValue( T obj_i )

{

}

template

T TestTemp::Getalue()

{

return m_Obj;

}

Client Source File

// Client.cpp

#include "TestTemp.h"

#include "TestTemp.cpp"

:

TestTemp TempObj;

TempObj.SetValue( 2 );

int nValue = TempObj.Getalue();

:

METHODE 3

You can #include the source file that implements your template class (TestTemp.cpp) in your header file that defines the template class (TestTemp.h) and remove the source file from the project, not from the folder.

Sample Code:

Template Class Header File

// TestTemp.h

#ifndef _TESTTEMP_H_

#define _TESTTEMP_H_

template

class TestTemp

{

public:

TestTemp();

void SetValue( T obj_i );

T Getalue();

private:

T m_Obj;

};

#include "TestTemp.cpp"

#endif

Template Class Source File

// TestTemp.cpp

#include "TestTemp.h"

template

TestTemp::TestTemp()

{

}

template

void TestTemp::SetValue( T obj_i )

{

}

template

T TestTemp::Getalue()

{

return m_Obj;

}

Client Source File

// Client.cpp

#include "TestTemp.h"

:

TestTemp TempObj;

TempObj.SetValue( 2 );

int nValue = TempObj.Getalue();

:

Saturday, December 19, 2009

MFC running procedure

When you run your program the kernel first calls this function, WinMainCRTStartup. WinMainCRTStartup first initializes the CRT routines. Then it parses the command line and removes the filename portion and calls WinMain passing the parsed command line as lpszCommandLine. _tWinMain is a macro that equates to wWinMain / Winmain based on the UNOCDE preprocessor.

#ifdef _UNICODE

#define _tWinMain wWinMain

#else /* ndef _UNICODE */

#define _tWinMain WinMain

#endif /* _UNICODE */

The WinMain will call AfxWinMain defined in winmain.cpp.

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);

int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();

// AFX internal initialization

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;

// App global initializations (rare)

if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;

// Perform specific initializations

if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls

if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif

AfxWinTerm();
return nReturnCode;
}

The functions AfxGetThread and AfxGetApp are used to get pointers to the CWinApp and CWinThread. AfxGetThread and AfxGetApp got their information from the CWinApp constructor in appcore.cpp

pThreadState->m_pCurrentWinThread = this;

pModuleState->m_pCurrentWinApp = this;
pThreadState is a AFX_MODULE_THREAD_STATE* and pModuleState is a AFX_MODULE_STATE*.

All MFC applications have at least two objects: an application object derived from CWinApp, and some sort of main window object, derived from CFrameWnd / CMDIFramend / CDialog, all of which are derived from CWnd. C++ programs will first create all global and static objects before execution begins. It will happen much before AfxWinMain gets called. There is only statement in the entire program that has global scope is the statement that instantiates the application object.

CWinApp derived class constructor gets called and the AFX_MODULE_STATE structure is setup properly. Now AfxWinInit is called and this function initializes the MFC framework. Here it calls the InitInstance of the CWinApp derived object.

Then CWinApp::Run is called. Within the Run function is implemented default message loop. The Run() acquires and dispatches Windows messages until the application receives a WM_QUIT message. After WM_QUIT message, Run returns and control returns to AfxWinMain which performs clean-up and lastly calls AfxWinTerm which deletes all the global application structures that were created.

The CObject class

CObject is the base class for MFC classes. The CObject class contains 3 main useful features
1. Serialization Support
2. Run Time class information support
3. Diagnostic and debugging support
The derived classes from cobject, can exploit these features

Sunday, September 27, 2009

Usage of ::Sleep(0)

The Sleep() function is normally used to put a thread to sleep for a particular time period. The time period is passed as the parameter to the Sleep() function. The statement Sleep(0) has even a hand on thread scheduler in case of multithreaded application. It can be explained with a small example that you have an application which fills 5 different colored balls uniformly in a bucket with 5 threads running at a time. Each thread is assigned with a color. If you just allow each thread to run in a loop, all the threads will try to grab the maximum CPU time it can get without bothering about the other threads running in parallel. This doesn't give a color uniformity for the filling process. you can maintain this uniformity by putting a Sleep(0) statement in each thread at the place where you have finished putting a single ball. This will relinquish the CPU for the next thread allowing it to put a ball into bucket.