#ifndef not working

Started by
23 comments, last by Hawkblood 10 years ago

Ok. I have decided to try my hand at DX11. I'm starting from scratch here because the "empty project" provided by the SDK has a butt-load of crap that I don't want to use right now (just want to learn the basics). Until now I've been using only one .cpp file and all others were .h files. Each class had 2 .h files, one for the header info and the other for the functions. I know this is bad practice, but it was the only way I could control the compile order. I WANT TO CORRECT THIS! So here is my project as it stands:

Main.cpp:

globals.h:


#ifndef GLOBALS_H
#define GLOBALS_H
#include <windows.h>  
#include <windowsx.h>

void Init3D(HWND hWnd);
void GameLoop(void);
void RenderFrame(void);
void ExitProgram(void);


float SCREEN_WIDTH =float(GetSystemMetrics( SM_CXSCREEN ));
float SCREEN_HEIGHT = float(GetSystemMetrics( SM_CYSCREEN ));


#include "Graphics.h"

#endif


Graphics.h:


#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3d11.lib")
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3dx11.lib")
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3dx10.lib")

IDXGISwapChain *swapchain;             // the pointer to the swap chain interface
ID3D11Device *dev;                     // the pointer to our Direct3D device interface
ID3D11DeviceContext *devcon;           // the pointer to our Direct3D device context

void InitD3D(HWND hWnd);     // sets up and initializes Direct3D
void CleanD3D(void);         // closes Direct3D and releases memory


Graphics.cpp:


#include "globals.h"

void InitD3D(HWND hWnd)
{
    // create a struct to hold information about the swap chain
    DXGI_SWAP_CHAIN_DESC scd;

    // clear out the struct for use
    ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

    // fill the swap chain description struct
    scd.BufferCount = 1;                                    // one back buffer
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
    scd.OutputWindow = hWnd;                                // the window to be used
    scd.SampleDesc.Count = 4;                               // how many multisamples
    scd.Windowed = TRUE;                                    // windowed/full-screen mode

    // create a device, device context and swap chain using the information in the scd struct
    D3D11CreateDeviceAndSwapChain(NULL,
                                  D3D_DRIVER_TYPE_HARDWARE,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  D3D11_SDK_VERSION,
                                  &scd,
                                  &swapchain,
                                  &dev,
                                  NULL,
                                  &devcon);
}
void CleanD3D()
{
    // close and release all existing COM objects
    swapchain->Release();
    dev->Release();
    devcon->Release();
}

The problem is that the compiler says that the stuff in globals.h is already defined:


Error	1	error LNK2005: "float SCREEN_WIDTH" (?SCREEN_WIDTH@@3MA) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	2	error LNK2005: "struct ID3D11DeviceContext * devcon" (?devcon@@3PAUID3D11DeviceContext@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	3	error LNK2005: "struct ID3D11Device * dev" (?dev@@3PAUID3D11Device@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	4	error LNK2005: "struct IDXGISwapChain * swapchain" (?swapchain@@3PAUIDXGISwapChain@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	5	error LNK2005: "float SCREEN_HEIGHT" (?SCREEN_HEIGHT@@3MA) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	6	error LNK1169: one or more multiply defined symbols found	C:\Empty DX11 Project1\Release\DX11Test.exe	Empty DX11 Project1

Doesn't the "#ifndef" stuff work? Am I using it wrong? HOW DO I GET IT TO "DEFINE" ONLY ONCE?


Advertisement

I can't seem to get main.cpp to copy onto the post. Here is another attempt:


#include "globals.h"
LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);  

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine,int nShow)
{
	MSG msg;
	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_VREDRAW|CS_HREDRAW|CS_OWNDC, 
		WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), 
		NULL, "DX11_CLASS", NULL}; 
	RegisterClassEx(&wc);
	HWND hMainWnd = CreateWindow("DX11_CLASS", 
		"DirectX 11 Bare Bones", 
		WS_EX_TOPMOST | WS_POPUP, 0, 0, int(SCREEN_WIDTH), int(SCREEN_HEIGHT), 
		NULL, NULL, hInstance, NULL);
	ShowWindow(hMainWnd, nShow);
	UpdateWindow(hMainWnd);
	Init3D(hMainWnd);
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	ExitProgram();
	return(0);
} 


LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){

	switch(msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return(0);


	case WM_PAINT: // <— ADD THIS BLOCK
		// drawing code goes here…
		GameLoop();
		ValidateRect(hwnd, NULL);
		return(0);


	case WM_CHAR:


		switch(wParam){
			case VK_ESCAPE:
				PostMessage(hwnd,WM_QUIT,0,0);
				break;
		}
		break;


	}

	return(DefWindowProc(hwnd, msg, wParam, lParam));
}


void Init3D(HWND hWnd){
//will be calling Graphics class to initialize 
}
void GameLoop(void){
	//check DX device
	//frame move
	//render
	RenderFrame();
}
void RenderFrame(void){
}
void ExitProgram(void){
// call Graphics class to clear device objects
}

I HAD TO BREAK IT UP IN CHUNKS FOR IT TO SHOW ON THE POST. WTF!

?

?

?

?

float SCREEN_WIDTH =float(GetSystemMetrics( SM_CXSCREEN ));
float SCREEN_HEIGHT = float(GetSystemMetrics( SM_CYSCREEN ));
These are variable definitions. C++ says you may only define a variable once, and because your header file is included in multiple locations you are getting those errors. The same goes for the globals in Graphics.h.

Note that the include guards are working fine. What include guards prevent is a given header file being included more than once during compilation of a given source file. The classic C++ compilation model is that each source file (.cpp) is compiled once in isolation. This involves running the preprocessor (which is a glorified automatic copy & paste tool) to create a so-called translation unit, which is then compiled to an object file (note: terminology pre-dates object-orientation, an "object" here is more or less any non-local C++ identifier). Interesting to note that the C++ compiler proper sees the output of the pre-processor, not the original code you wrote! Finally, when that process completes for all source files, the linker is invoked on the object files, which fixes up references between them to produce an executable.

So to clarify the problem, each source file compiles perfectly, but each object file ends up containing a SCREEN_WIDTH and SCREEN_HEIGHT object. The linker is confused, it has no way to know that these are really the "same" objects (the information is literally lost in translation!), it complains just as if you accidentally created two separate globals with the same name, perhaps with separate values, in two source files.

Note there are special rules for certain things, such as const integral values, inline functions or member functions defined in a class body, and static values.

The solution is to follow the rules in Organizing code files in C and C++, in this case declare these variables in a header file and define them in a single source file. Alternative solutions might be to convert some values to inline functions, or try to avoid using globals altogether.

Ok. I declared them in globals.h and defined them in Graphics.cpp within void Init3D(HWND hWnd)

I get this:


Error	1	error LNK2005: "float SCREEN_WIDTH" (?SCREEN_WIDTH@@3MA) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	2	error LNK2005: "struct ID3D11DeviceContext * devcon" (?devcon@@3PAUID3D11DeviceContext@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	3	error LNK2005: "struct ID3D11Device * dev" (?dev@@3PAUID3D11Device@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	4	error LNK2005: "float SCREEN_HEIGHT" (?SCREEN_HEIGHT@@3MA) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	5	error LNK2005: "struct IDXGISwapChain * swapchain" (?swapchain@@3PAUIDXGISwapChain@@A) already defined in Grapgics.obj	C:\Empty DX11 Project1\main.obj	Empty DX11 Project1
Error	6	error LNK1169: one or more multiply defined symbols found	C:\Empty DX11 Project1\Release\DX11Test.exe	Empty DX11 Project1

Which, to me, looks the same....... Also, I can't find the article you linked me to. The link just goes to a list of articles. I searched through them and didn't find the one you were pointing me to.

Sorry, had some issues with the editor, the link should be fixed now. Can we see your current code?

Use headers for declarations only and define in source files.

Declare this way:


// Keyword "extern" basically means "declare something that's defined somewhere else".
extern float SCREEN_WIDTH;
extern float SCREEN_HEIGHT;

extern IDXGISwapChain *swapchain;
extern ID3D11Device *dev;
extern ID3D11DeviceContext *devcon;

Define this way:


float SCREEN_WIDTH = float(GetSystemMetrics(SM_CXSCREEN));
float SCREEN_HEIGHT = float(GetSystemMetrics(SM_CYSCREEN));

// Always initialize your variables, in this case a pointer to null.
IDXGISwapChain *swapchain = nullptr;
ID3D11Device *dev = nullptr;
ID3D11DeviceContext *devcon = nullptr;

Also, you forgot a header guard in the "Graphics.h".

I hope I corrected the issues with "main.cpp" that kept me from posting the code. Here is the attempt:

main.cpp:


#include "stdafx.h"
#include "globals.h"

LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);  
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine,int nShow)
{
	MSG msg;
	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_VREDRAW|CS_HREDRAW|CS_OWNDC, 
		WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), 
		NULL, "DX11_CLASS", NULL}; 
	RegisterClassEx(&wc);
	HWND hMainWnd = CreateWindow("DX11_CLASS", 
		"DirectX 11 Bare Bones", 
		WS_EX_TOPMOST | WS_POPUP, 0, 0, int(SCREEN_WIDTH), int(SCREEN_HEIGHT), 
		NULL, NULL, hInstance, NULL);
	ShowWindow(hMainWnd, nShow);
	UpdateWindow(hMainWnd);
	Init3D(hMainWnd);
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	ExitProgram();
	return(0);
} 
LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
	switch(msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return(0);
	case WM_PAINT: // <— ADD THIS BLOCK
		// drawing code goes here…
		GameLoop();
		ValidateRect(hwnd, NULL);
		return(0);
	case WM_CHAR:
		switch(wParam){
			case VK_ESCAPE:
				PostMessage(hwnd,WM_QUIT,0,0);
				break;
		}
		break;
	}

	return(DefWindowProc(hwnd, msg, wParam, lParam));
}
void Init3D(HWND hWnd){
//will be calling Graphics class to initialize 
	Graphics.InitD3D(hWnd,SCREEN_WIDTH,SCREEN_HEIGHT);
}
void GameLoop(void){
	//check DX device
	//frame move
	//render
	RenderFrame();
}
void RenderFrame(void){
	Graphics.devcon->ClearRenderTargetView(Graphics.backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));



	Graphics.swapchain->Present(0, 0);
}
void ExitProgram(void){
// call Graphics class to clear device objects
	Graphics.CleanD3D();
}

stdafx.h:


#include <windows.h>  
#include <windowsx.h>

void Init3D(HWND hWnd);
void GameLoop(void);
void RenderFrame(void);
void ExitProgram(void);


#include "Graphics.h"

Graphics.h:


#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3d11.lib")
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3dx11.lib")
#pragma comment (lib, "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)/Lib/x86/d3dx10.lib")

struct GRAPHICS{
	IDXGISwapChain *swapchain;             // the pointer to the swap chain interface
	ID3D11Device *dev;                     // the pointer to our Direct3D device interface
	ID3D11DeviceContext *devcon;           // the pointer to our Direct3D device context

	ID3D11RenderTargetView *backbuffer;


	void InitD3D(HWND hWnd,float SW,float SH);     // sets up and initializes Direct3D
	void CleanD3D(void);         // closes Direct3D and releases memory
};

Graphics.cpp:


#include "stdafx.h"


void GRAPHICS::InitD3D(HWND hWnd,float SW,float SH)
{
    // create a struct to hold information about the swap chain
    DXGI_SWAP_CHAIN_DESC scd;

    // clear out the struct for use
    ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

    // fill the swap chain description struct
    scd.BufferCount = 1;                                    // one back buffer
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
    scd.OutputWindow = hWnd;                                // the window to be used
    scd.SampleDesc.Count = 4;                               // how many multisamples
    scd.Windowed = TRUE;                                    // windowed/full-screen mode

    // create a device, device context and swap chain using the information in the scd struct
    D3D11CreateDeviceAndSwapChain(NULL,
                                  D3D_DRIVER_TYPE_HARDWARE,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  D3D11_SDK_VERSION,
                                  &scd,
                                  &swapchain,
                                  &dev,
                                  NULL,
                                  &devcon);

	// get the address of the back buffer
	ID3D11Texture2D *pBackBuffer;
	swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
	// use the back buffer address to create the render target
	dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
	pBackBuffer->Release();
	// set the render target as the back buffer
	devcon->OMSetRenderTargets(1, &backbuffer, NULL);

	D3D11_VIEWPORT viewport;
	ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	viewport.Width = SW;
	viewport.Height = SH;
	devcon->RSSetViewports(1, &viewport);

}
void GRAPHICS::CleanD3D()
{
    // close and release all existing COM objects
    swapchain->Release();
	backbuffer->Release();
    dev->Release();
    devcon->Release();
}

globals.h:


#include <windows.h>  
#include <windowsx.h>

float SCREEN_WIDTH =float(GetSystemMetrics( SM_CXSCREEN ));
float SCREEN_HEIGHT = float(GetSystemMetrics( SM_CYSCREEN ));

GRAPHICS Graphics;

I modified the project so that I wouldn't get the errors. Now my concern is "how will this screw up my project?"

You'd need to amend globals.h to be like so:

#include <windows.h>  
#include <windowsx.h>

extern float SCREEN_WIDTH;
extern float SCREEN_HEIGHT;

extern GRAPHICS Graphics;
And to add a globals.cpp (or to another file such as main.cpp):

#include "globals.h"

float SCREEN_WIDTH = float(GetSystemMetrics( SM_CXSCREEN ));
float SCREEN_HEIGHT = float(GetSystemMetrics( SM_CYSCREEN ));

GRAPHICS Graphics;
You'll probably need to #include "graphics.h" in globals.h too.

First, I would recommend that you NOT declare functions in stdafx.h. Any time you change anything in stdafx.h, you'll likely have to recompile the entire project.

I.e., these forward declarations belong in a header (e.g., main.h - which you don't appear to have) for main.cpp, where those functions are defined.


void Init3D(HWND hWnd);
void GameLoop(void);
void RenderFrame(void);
void ExitProgram(void);

If, for instance, you just want to add an exit code and change ExitProgram(void) to ExitProgram(int returnCode), you'll have to recompile every cpp file that includes stdafx.h, whether ExitProgram is called in that cpp file or not.

In general, header files describe what can be found in an associated code file. E.g., graphics.h describes what can be found in graphics.cpp; commonStuff.h would describe what's in commonStuff.cpp, etc.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


First, I would recommend that you NOT declare functions in stdafx.h. Any time you change anything in stdafx.h, you'll likely have to recompile the entire project.

That's not a real concern right now, but I moved them to main.cpp anyway....

@rip-off

I tried that. I got lots of errors. So far the only place I need any kind of global variables is in main.cpp, so I made a "main.h" and placed all those variables and function definitions in that.

This topic is closed to new replies.

Advertisement