Troubles using .h and .cpp files

Started by
7 comments, last by Pto 14 years, 5 months ago
Hi there, I am currently working on a project where the code is starting to build up in a class. In the past I have always crammed the functions into the header file. I know this is bad practice and this is why I am trying to do it the right way. At the moment I have a function prototype in the .h file after the 'public:' statement. I have put the actual function in a separate .cpp file like so. engine.h

#pragma once

class Engine
{
public:
	void function();

engine.cpp

#include engine.h

void function()
{
	// implementation here
}

The problem is when I compile I get a linker error as a result
Quote: 1>framework.obj : error LNK2005: "long __stdcall WinProc(struct HWND__ *,unsigned int,unsigned int,long)" (?WinProc@@YGJPAUHWND__@@IIJ@Z) already defined in main.obj 1>framework.obj : error LNK2005: "private: static int Engine::init" (?init@Engine@@0HA) already defined in main.obj
I cant figure out what is happening here. Any help would be greatly appreciated.
Advertisement
The linker is complaining because it is seeing definitions for the same things in different files, namely WinProc and Engine::init in both main.cpp and framework.cpp.

Are these functions/static member definitions in a header file main.cpp and framework.cpp are both including?

Also, you might already be aware of this, and just forgot it when typing out your example case code here without compiling, but your engine.cpp would look like:
#include "engine.h"void Engine::function(){	// implementation here}
I.e., you need to explicitly include the class name in the method definition.
You are right about the example I did leave off Engine:: at the front. My actual code has it in there though.

The function is only defined in the .h file.

If I inline the code it works fine. So, I cant see where the re-definition is happening.
Quote:Original post by lonewolff
The function is only defined in the .h file.

If I inline the code it works fine. So, I cant see where the re-definition is happening.

Which function?

Post the source for the header you're talking about.
You might want to post ALL of the code you have, not just the 14 lines shown above.
Here is the full source. The function in question is Engine::startRender() - as that is where I started to put the stuff in the cpp file.

main.cpp
#include"framework.h"int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd){	Engine *mEngine=new Engine;	mEngine->makeWindow();	mEngine->terraForm(10,10);	MSG msg;	ZeroMemory(&msg,sizeof(msg));	while(msg.message!=WM_QUIT)	{		if(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))		{			TranslateMessage(&msg);			DispatchMessage(&msg);		}		else		{			mEngine->startRender();							// ***** Put scene here *****				mEngine->cameraPosition(0,10.0,-15.0);				mEngine->renderTerrain();			mEngine->endRender();			if(mEngine->handle()==GetActiveWindow())			{				if(GetAsyncKeyState(VK_ESCAPE))				{					PostQuitMessage(0);				}			}		}	}	delete mEngine;	return 0;}


framework.h
#pragma once#include <d3dx9.h>#pragma comment(lib,"d3d9.lib")#pragma comment(lib,"d3dx9.lib")#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)struct CUSTOMVERTEX{FLOAT X,Y,Z;DWORD COLOR;};LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){	switch(msg)	{		case WM_DESTROY:		{			PostQuitMessage(0);			return 0;		}		break;	}	return DefWindowProc(hWnd,msg,wParam,lParam);}class Engine{public:	Engine()	{		if(init==0)		{			hWnd=NULL;			d3dInterface=NULL;			d3dDevice=NULL;			v_buffer=NULL;			i_buffer=NULL;		}		++(this->init);	}	~Engine()	{		--(this->init);		if(init==0)		{			if(d3dDevice)				d3dDevice->Release();			if(d3dInterface)				d3dInterface->Release();		}	}	int makeWindow()	{		WNDCLASSEX wClass;		ZeroMemory(&wClass,sizeof(WNDCLASSEX));		wClass.cbClsExtra=NULL;		wClass.cbSize=sizeof(WNDCLASSEX);		wClass.cbWndExtra=NULL;		wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;		wClass.hCursor=LoadCursor(NULL,IDC_ARROW);		wClass.hIcon=NULL;		wClass.hIconSm=NULL;		wClass.hInstance=(HINSTANCE)GetModuleHandle(NULL);		wClass.lpfnWndProc=(WNDPROC)WinProc;		wClass.lpszClassName="Window Class";		wClass.lpszMenuName=NULL;		wClass.style=CS_HREDRAW|CS_VREDRAW;			if(!RegisterClassEx(&wClass))		{			int nResult=GetLastError();			MessageBox(NULL,				"Window class creation failed",				"Window Class Failed",				MB_ICONERROR);			return nResult;		}		hWnd=CreateWindowEx(NULL,				"Window Class",				"Window Title",				WS_OVERLAPPEDWINDOW,				(GetSystemMetrics(SM_CXSCREEN)/3)*2-(GetSystemMetrics(SM_CXSCREEN)/2),				(GetSystemMetrics(SM_CYSCREEN)/3)*2-(GetSystemMetrics(SM_CYSCREEN)/2),				(GetSystemMetrics(SM_CXSCREEN)/3)*2,				(GetSystemMetrics(SM_CYSCREEN)/3)*2,				NULL,				NULL,				(HINSTANCE)GetModuleHandle(NULL),				NULL);			if(!hWnd)		{			int nResult=GetLastError();				MessageBox(NULL,				"Window creation failed",				"Window Creation Failed",				MB_ICONERROR);			return nResult;		}			// Setup D3D Interface		d3dInterface=Direct3DCreate9(D3D_SDK_VERSION);		if(!d3dInterface)		{			MessageBox(NULL,"Could not create D3D interface","Initialization problem!",NULL);			return 1;		}			// Setup D3D adapter		D3DPRESENT_PARAMETERS d3dpp;		d3dpp.AutoDepthStencilFormat=D3DFMT_D24S8;		d3dpp.BackBufferCount=1;		d3dpp.BackBufferFormat=D3DFMT_X8R8G8B8;		d3dpp.BackBufferHeight=(GetSystemMetrics(SM_CYSCREEN)/3)*2;		d3dpp.BackBufferWidth=(GetSystemMetrics(SM_CXSCREEN)/3)*2;		d3dpp.EnableAutoDepthStencil=true;		d3dpp.Flags=0;		d3dpp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;		d3dpp.hDeviceWindow=hWnd;		d3dpp.MultiSampleQuality=0;		d3dpp.MultiSampleType=D3DMULTISAMPLE_NONE;		d3dpp.PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE;		d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;		d3dpp.Windowed=true;			// Create D3D Device		d3dInterface->CreateDevice(D3DADAPTER_DEFAULT,										D3DDEVTYPE_HAL,										hWnd,										D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_PUREDEVICE,										&d3dpp,										&d3dDevice);			if(!d3dDevice)		{			MessageBox(NULL,"Unable to create D3D device.\r\nYour graphics adapter may not support 'Hardware Vertex Processing'","Initialization problem!",NULL);			return 1;		}			d3dDevice->SetRenderState(D3DRS_LIGHTING,FALSE);			// Show the window after all of the DX initialization for a faster cleaner looking start-up		ShowWindow(hWnd,1);			return 0;	}	void startRender(void);/*	void startRender()	{		d3dDevice->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);		d3dDevice->BeginScene();	}*/	void endRender()	{		d3dDevice->EndScene();		d3dDevice->Present(0,0,0,0);	}	HWND handle()	{		return hWnd;	}	void createTerrain()	{		return;	}	void cameraPosition(float x,float y,float z)	{		D3DXMATRIX mCamera;		D3DXVECTOR3 mCameraPosition(x,y,z);		D3DXVECTOR3 mCameraUp(0.0f,1.0f,0.0f);		D3DXVECTOR3 mCameraTarget(0.0f,0.0f,0.0f);		D3DXMatrixLookAtLH(&mCamera,&mCameraPosition,&mCameraTarget,&mCameraUp);		d3dDevice->SetTransform(D3DTS_VIEW,&mCamera);		D3DXMATRIX mFrustum;		RECT rRect;		GetClientRect(hWnd,&rRect);		float width=(float)rRect.right;		float height=(float)rRect.bottom;		D3DXMatrixPerspectiveFovLH(&mFrustum,D3DXToRadian(60),((FLOAT)(GetSystemMetrics(SM_CXSCREEN)/3)*2)/((FLOAT)(GetSystemMetrics(SM_CYSCREEN)/3)*2),1.0f,5000.0f);		d3dDevice->SetTransform(D3DTS_PROJECTION,&mFrustum);		return;	}	void terraForm(int width,int height)	{		CUSTOMVERTEX vertices[100];		int k=0;		for(float i=0;i<height;++i)		{			for(float j=0;j<width;++j)			{				vertices[k].X=j;				vertices[k].Y=0.0f;				vertices[k].Z=-i;				vertices[k].COLOR=0xffffffff;				++k;			}		}		d3dDevice->CreateVertexBuffer(100*sizeof(CUSTOMVERTEX),0,CUSTOMFVF,D3DPOOL_MANAGED,&v_buffer,NULL);		VOID* pVoid;    // a void pointer		v_buffer->Lock(0,0,(void**)&pVoid,0);	// lock v_buffer and load the vertices into it		memcpy(pVoid,vertices,sizeof(vertices));		v_buffer->Unlock();		short indices[3*100];		k=0;		for(int i=0;i<height-1;++i)		{			for(int j=0;j<width-1;++j)			{				// Triange 1				indices[k]=i*height+j;				indices[k+1]=i*height+j+1;				indices[k+2]=(i+1)*height+j;				// Trangle 2				indices[k+3]=(i+1)*height+j;				indices[k+4]=i*height+j+1;				indices[k+5]=(i+1)*height+j+1;				k+=6;			}		}		// create an index buffer interface called i_buffer		d3dDevice->CreateIndexBuffer(((width-1)*(height-1)*2*3)*sizeof(short),0,D3DFMT_INDEX16,D3DPOOL_MANAGED,&i_buffer,NULL);	// 6 is the number of indices      Quads = tri's * 2		i_buffer->Lock(0,0,(void**)&pVoid,0);	// lock i_buffer and load the indices into it		memcpy(pVoid,indices,sizeof(indices));		i_buffer->Unlock();		return;	}	void renderTerrain()	{		// Prepare to render the terrain		d3dDevice->SetFVF(CUSTOMFVF);		// select the vertex buffer to display		d3dDevice->SetStreamSource(0,v_buffer,0,sizeof(CUSTOMVERTEX));		d3dDevice->SetIndices(i_buffer);		// Draw the terrain		//g_d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,10,0,6);		d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,100,0,(10-1)*(10-1)*2);	}private:	static int init;	HWND hWnd;	IDirect3D9 *d3dInterface;			// Remember anything starting with 'I' is COM	IDirect3DDevice9 *d3dDevice;		// object and requires releasing at end of app.	LPDIRECT3DVERTEXBUFFER9 v_buffer;			// Pointer to the vertex buffer	LPDIRECT3DINDEXBUFFER9 i_buffer;			// Pointer to the index buffer};int Engine::init=0;


famework.cpp
#include"framework.h"void Engine::startRender(){	d3dDevice->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);	d3dDevice->BeginScene();}


Once again, thanks in adavnce!
Since WinProc is defined in framework.h, which is included in both main.cpp and framework.cpp, the function will be defined in both main.cpp and framework.cpp also. This is what the linker error says, that the function is defined in multiple places. Same for the Engine::init definition.

Move the definition to one source file only, and only use declarations in header files.
Cool, I got you.

BTW, isnt this what #pragma once is for?
Quote:Original post by lonewolff
BTW, isnt this what #pragma once is for?


No. That ensures that its only included once per .cpp file, NOT across all files. So if you used #pragmaonec WinProc is included once in main.cpp, and then once in framework.cpp which doesn't help you at all.


What you want is to have something like:

// framework.h// this is just a declaration saying "hey guys, this function exists"// and can be included as many times as you like error freeLRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);// then in framework.cpp you have the full thingLRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){	switch(msg)	{		......	}}



Remember the compiler builds ever single .cpp file into an object file. Then the linker takes those object files and mash's them together, but it gets confused if it has 2 functions of the same name in different object files. When you call winproc which one did you mean? The one from framework or the one from main? The linker can't tell so you get an error. A easy rule is - never have a function implementation in a .h, only have declarations then put the implementations in the corresponding cpp file.

This topic is closed to new replies.

Advertisement