Sign in to follow this  
mrmrcoleman

Inheritance linker problems...

Recommended Posts

I have the following classes. The first one is called Screen and serves as a base class, here is the cpp file.
#include "Screen.h"

// Object used to compare screen entity objects
//struct ScreenEntityLesserComparator : binary_function<ScreenEntityBase*, ScreenEntityBase*, bool>
//{
//	bool operator()(ScreenEntityBase* lhs, ScreenEntityBase* rhs)
//	{
//		return lhs->GetDepthLevel() < rhs->GetDepthLevel();
//	}
//} ScreenEntityLessThan;

// Default constructor
Screen::Screen()
{
	// Set the flags to indicate that the screen is not initialised
	ScreenEntityList.clear();
	Flags = 0;
}

// Copy constructor
Screen::Screen(Screen& rhs)
{
	// Check for self assignment
	if(this == &rhs)
		return;

	// Copy the flags member
	Flags = rhs.GetFlags();

	// Now copy the Vector, loop through and copy the entities
	// If the vector is empty then initialise an empty vector
	// otherwise copy the the vector over
	if(rhs.IsEmpty())
	{
		ScreenEntityList.clear();
	}
	else
	{
		// Clear the target vector, then copy each element
		ScreenEntityList.clear();
		for(vector<ScreenEntityBase*>::iterator source = rhs.GetScreenEntityList().begin();source != rhs.GetScreenEntityList().end();source++)
		{
			ScreenEntityList.push_back(*source);
		}	
	}
}
// Deconstructor
Screen::~Screen()
{
	// Need to delete all the items in the list
	// To do this loop through the list and delete
	// each of the screen objects first. Then the
	// list can be cleared
	
	// if the list has no elements then do nothing
	if(!(ScreenEntityList.empty()))
	{
		for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
		{
			ScreenEntityList.erase(walker);
		}
	}

	// Clear the list
	ScreenEntityList.clear();
}

// Overides the assignment operator
Screen& Screen::operator=(Screen& rhs)
{
	// Check for self assignment
	if(this == &rhs)
		return *this;

	// Copy the flags member
	Flags = rhs.GetFlags();

	// Now copy the Vector, loop through and copy the entities
	// If the vector is empty then initialise an empty vector
	// otherwise copy the the vector over
	if(rhs.IsEmpty())
	{
		ScreenEntityList.clear();
	}
	else
	{
		// Clear the target vector, then copy each element
		ScreenEntityList.clear();
		for(vector<ScreenEntityBase*>::iterator source = rhs.GetScreenEntityList().begin();source != rhs.GetScreenEntityList().end();source++)
		{
			ScreenEntityList.push_back(*source);
		}	
	}

	// The items have been inserted in the correct order so return as is
	return *this;
}

// Adds a ScreenEntity to the screen list
HRESULT Screen::AddScreenEntity(ScreenEntityBase* newScreenEntity)
{
	// Add the screen entity to the list
	ScreenEntityList.push_back(newScreenEntity);
	
	// Now sort the list to preserve the depth order
	//sort(ScreenEntityList.begin(), ScreenEntityList.end(), ScreenEntityLessThan);

	return S_OK;
}

// Returns the Flags member of the object
DWORD Screen::GetFlags()
{
	return Flags;
}

// Returns true if the ScreenEntityList is empty
bool Screen::IsEmpty()
{
	return ScreenEntityList.empty();
}

// Returns a reference to the ScreenEntityList
vector<ScreenEntityBase*>& Screen::GetScreenEntityList()
{
	return ScreenEntityList;
}

// Attempts to remove the specified screen entity from the list
HRESULT Screen::RemoveScreenEntity(int ScreenEntityID)
{
	bool found = false;
	vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();

	while(walker != ScreenEntityList.end() && found == false)
	{
		if((*walker)->GetScreenEntityID() == ScreenEntityID)
		{
			// Found the entity
			// Fade out the entity
			(*walker)->SetFadeDirection(FADE_OUT_X5);

			// Make the entity temporary so that it is
			// destroyed when the next key is pressed
			(*walker)->AddFlags(SCREENENTITY_TEMPORARY);

			found = true;
		}
		// Increment the iterator
		walker++;
	}

	if(found)
		return S_OK;
	else
		return S_FALSE;
}

long GetJulianDate(int Year, int Month, int Day)
{
	int A = (int)(7 * (Year + (int)((Month + 9)/12))/4);
	int B = (int)((275 * Month)/9);

	return (367 * Year) - A + B + Day + 1721013.5;
}

void Screen::AddFlags(DWORD newFlags)
{
	Flags = Flags | newFlags;
}

ScreenEntityBase* Screen::FindEntity(string EntityDescription)
{
	for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
	{
		if(strcmp((*walker)->GetBehaviourModifier().c_str(), EntityDescription.c_str()) == 0)
		{
			
			return (*walker);
		}
	}
	
	// We have not found the entity, so return 0
	return 0;
}

// Attempts to remove the specified screen entity from the list
HRESULT Screen::FadeScreenEntity(int ScreenEntityID, int FadeVector)
{
	for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
	{
		if((*walker)->GetScreenEntityID() == ScreenEntityID)
		{
			// Found the entity
			// Set the correct fade direction
			(*walker)->SetFadeDirection(FadeVector);		
		}
	}
	return S_OK;
}

// Loads a 32bpp TGA file into a bitmap handle, can also perform the pre-multiply step
HBITMAP Load32bppTga(const TCHAR * pFileName, bool bPreMultiply)
{
    HANDLE handle = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, 
	                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	
    if ( handle == INVALID_HANDLE_VALUE )
        return NULL;

    TGA_Header header;

    DWORD dwRead = 0;
    ReadFile(handle, & header, sizeof(header), & dwRead, NULL);

    if ( (header.IDLength!=0) || (header.ColorMapType!=0) || (header.ImageType!=2) ||
         (header.PixelDepth!=32) || (header.ImageDescriptor!=8) )
    {
        CloseHandle(handle);
        return NULL;
    }

    BITMAPINFO bmp = { { sizeof(BITMAPINFOHEADER), header.Width, header.Height, 1, 32 } };

    void * pBits = NULL;

    HBITMAP hBmp = CreateDIBSection(NULL, & bmp, DIB_RGB_COLORS, & pBits, NULL, NULL);

    if ( hBmp==NULL )
    {
        CloseHandle(handle);
        return NULL;
    }

    ReadFile(handle, pBits, header.Width * header.Height * 4, & dwRead, NULL);
	
    CloseHandle(handle);

    if ( bPreMultiply )
    {
        for (int y=0; y<header.Height; y++)
        {
            BYTE * pPixel = (BYTE *) pBits + header.Width * 4 * y;

            for (int x=0; x<header.Width; x++)
            {
                pPixel[0] = pPixel[0] * pPixel[3] / 255; 
                pPixel[1] = pPixel[1] * pPixel[3] / 255; 
                pPixel[2] = pPixel[2] * pPixel[3] / 255; 

                pPixel += 4;
            }
        }
    }

    return hBmp;
}

bool IsLeapYear(int year)
{
	if((year%4) != 0)
		return false;
	else if((year%400) == 0)
		return true;
	else if((year%100) == 0)
		return false;
	else
		return true;
}

And here is the .h file..
#ifndef SCREEN_H
#define SCREEN_H

#include "ScreenEntityAnimation.h"
#include "ScreenEntityStaticImage.h"
#include "ScreenEntityText.h"
#include "ScreenEntityParticleSystem.h"

// Screen class
class Screen
{
public:
	// (De)Constructors
	Screen();
	Screen(Screen& rhs);
	virtual ~Screen();

	// Over-ridden operators
	Screen& Screen::operator=(Screen & rhs);

	// Public member functions	
	virtual HRESULT HandleMessage(UINT MessageType, WPARAM wparam, LPARAM lparam) = 0; // Handles windows messages
	virtual HRESULT Initialise() = 0; // Initialises all data members
	HRESULT AddScreenEntity(ScreenEntityBase* newScreenEntity); // Adds a screen entity to the screen
	virtual void OutputScreenInformation() = 0; // Outputs each of the screen entities in order
	DWORD GetFlags(); // Returns the Flags member of the object
	virtual HRESULT RenderScreen() = 0; // Renders all the elements in the screen entity list
	bool IsEmpty(); // Returns true if the screen entity list is empty
	vector<ScreenEntityBase*>& GetScreenEntityList();// Returns a reference to the ScreenEntityList
	HRESULT RemoveScreenEntity(int ScreenEntityID); // Attempts to remove the specified screen entity from the list
	virtual void NextScreen() = 0; // Closes down all the dynamic entities in the interface
	virtual void StartScreen(); // Start all create running animations and sounds
	void AddFlags(DWORD newFlags); // Adds new flags to the screen object
	ScreenEntityBase* FindEntity(string EntityDescription); // Returns a reference to an entity
	HRESULT FadeScreenEntity(int ScreenEntityID, int FadeVector); // Attempts to fade the specified screen entity at the requested fade speed

protected:
	// Private data members
	DWORD Flags; // Flags holds state information about the screen object
	vector<ScreenEntityBase*> ScreenEntityList; // List of the screen entities
};

#endif

And here is the class which is derived from the above: cpp file
#include "ExplanationScreen.h"

ExplanationScreen::ExplanationScreen()
									:Screen()
{
}

//ExplanationScreen::ExplanationScreen(ExplanationScreen & rhs)
//													:Screen(ExplanationScreen & rhs)
//{
//}

//ExplanationScreen::~ExplanationScreen()
//									:~Screen()
//{
//}

HRESULT ExplanationScreen::HandleMessage(UINT MessageType, WPARAM wparam, LPARAM lparam)
{
	switch (MessageType)
    {
		case WM_PAINT:
		case WM_TIMER:
			// The screen needs to be rendered. The ScreenEntityList is checked
			// in the render method so just call it blindly.
			this->RenderScreen();
			break;

		case WM_LBUTTONDOWN:
			this->HandleMouseEvent(MessageType, wparam, lparam);
			break;

		case WM_GRAPHNOTIFY:
			this->HandleGraphEvent(MessageType, wparam, lparam);
			break;

		case WM_STARTSCREEN:
			this->StartScreen();
			break;
	}
	return S_OK;
}

HRESULT ExplanationScreen::HandleMouseEvent(UINT MessageType, WPARAM wparam, LPARAM lparam)
{
	// There has been a mouse click so pass the message to each entity that
	// the mouse click position coincides with. This is done as a for loop
	// becuase the click could be over more than one entity on the screen.
	// Therefore the message is passed to all the entities that have been
	// touched and the entities themselves decide what action to take. This
	// should mean (in a well designed interface) that only one entity will
	// end up taking any action based on the content of the message.

	// If the list is empty then just return with nothing
	if(ScreenEntityList.empty())
		return S_OK;

	bool found = false;
	vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();
	while((walker != ScreenEntityList.end()) && (found == false))
	{
		if((*walker)->InRectangle(LOWORD(lparam), HIWORD(lparam)))
		{			
			// If the object is an input object then react accordingly
			if((*walker)->IsInputEntity())
			{
				// Pass the message on to the affected screen entity
				if((*walker)->IsValid())
				{
					(*walker)->HandleMessage(MessageType, wparam, lparam);

					// Call the input handling method and pass to it
					// the screen entity's behaviour modifier object
					this->HandleInputEntity((*walker)->GetBehaviourModifier());
				}
				else
				{
					DXTRACE_MSG("Invalid object, cannot be used as an input entity");
					(*walker)->OutputInformation();
				}

				// Set found to true so that the walker object does not become invalid
				found = true;
			}
		}
		
		// Increment the walker
		walker++;
	}

	return S_OK;
}

HRESULT ExplanationScreen::HandleGraphEvent(UINT MessageType, WPARAM wparam, LPARAM lparam)
{
	// One of the animations has produced a message
	// so find out which one and pass the message on
	bool found = false;
			
	// If the list is empty then just return with nothing
	if(ScreenEntityList.empty())
		return S_OK;

	// Loop through and pass the message on to the screen entity with the
	// matching entity ID, the graph message is then processed within the target object
	vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();
	while(walker != ScreenEntityList.end() && found == false)
	{
		if((*walker)->GetScreenEntityID() == lparam)
		{
			if(strcmp((*walker)->GetBehaviourModifier().c_str(), "Next") == 0)
			{
				// Pass on the message to the entity itself
				(*walker)->HandleMessage(MessageType, wparam, lparam);

				if(!(((*walker)->GetFlags() & ANIMATION_RUNNING) > 0))
				{
					if((Flags & SCREEN_WAITINGFORNEXT) > 0)
					{
						// The next button has ended animating so send a message
						// to the interface to move it on
						PostMessage(g_hWnd,	WM_NEXTSCREEN, 0, 0);
					
						// Reset the flag to not waiting
						Flags = Flags ^ SCREEN_WAITINGFORNEXT;
					}
				}

				// Set to true so that the loop stops
				found = true;
			}
			else
			{
				// There should only be one input entity on the explanation screen
				DXTRACE_MSG("Invalid input entity");
				exit(0);
				
				// Set to true so that the loop ends
				found = true;
			}
		}
				
		// Increment the iterator
		walker++;
	}
	return S_OK;
}

HRESULT ExplanationScreen::HandleInputEntity(string InputDescriptor)
{
	// There is no screen output area, but this screen will still probably have
	// a next button so we deal with this here
	if(strcmp(InputDescriptor.c_str(), "Next") == 0)
	{
		// Set the waiting flag so that we know we are just waiting for the next
		// button animation to finish, then we post the next screen message
		Flags = Flags | SCREEN_WAITINGFORNEXT;

		// Play the woosh sound
		if(FindEntity(string("Whoosh")))
		{
			static_cast<ScreenEntityAnimation*>(FindEntity(string("Whoosh")))->ControlEntity(true);
		}

		// Stop the torches from animating
		// Torch 1
		if(FindEntity(string("Particle Fire")))
		{
			static_cast<ScreenEntityParticleSystem*>(FindEntity(string("Particle Fire")))->ControlEntity(false);
			static_cast<ScreenEntityParticleSystem*>(FindEntity(string("Particle Fire")))->Release();
		}
				
		// Torch 2
		if(FindEntity(string("Particle Fire1")))
		{
			static_cast<ScreenEntityParticleSystem*>(FindEntity(string("Particle Fire1")))->ControlEntity(false);
			static_cast<ScreenEntityParticleSystem*>(FindEntity(string("Particle Fire1")))->Release();
		}

		// Now stop the sound of the torches playing
		if(FindEntity(string("Fire Sound")))
		{
			static_cast<ScreenEntityAnimation*>(FindEntity(string("Fire Sound")))->ControlEntity(false);
		}

		// Switch off the lit texture
		if(FindEntity(string("Background")))
		{
			static_cast<ScreenEntityStaticImage*>(FindEntity(string("Background")))->SetCurrentAlpha(MIN_ALPHA);
		}

		// Switch on the unlit background
		if(FindEntity(string("Unlit Background")))
		{
			static_cast<ScreenEntityStaticImage*>(FindEntity(string("Unlit Background")))->SetCurrentAlpha(MAX_ALPHA);
		}
	}
	else
	{
		// There should'nt be any other input entities on the explanation screen so
		// output to the debug stream and post a quit message
		DXTRACE_MSG("Invalid input entity");
		exit(0);
	}
	return S_OK;
}

HRESULT ExplanationScreen::Initialise()
{
	// if the list has no elements then do nothing
	if(ScreenEntityList.empty())
		return S_FALSE;

	// Assume that the list is already sorted in ascending order so
	// simply initialise each of the entities in the vector
	for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
	{
		// If the entity is valid then render it, otherwise intialise it and then render it
		if((!(*walker)->IsValid()))
		{
			(*walker)->Initialise();
		}
	}

	// Set the global alpha to full
	// Set the global alpha
	GlobalDirect3D9Device->SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_ARGB(MAX_ALPHA, 0, 0, 0));

	// return ok
	return S_OK;
}

void ExplanationScreen::OutputScreenInformation()
{
	// If the screen object has no entities then say so and return
	if(ScreenEntityList.empty())
	{
		DXTRACE_MSG("No information to output");
	}
	else
	{
		// Walk through the vector and call OutputInformation on each entity
		for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
		{
			(*walker)->OutputInformation();
		}
	}
}

HRESULT ExplanationScreen::RenderScreen()
{
	// if the list has no elements then do nothing
	if(ScreenEntityList.empty())
		return false;

	// if direct3d is not initialised then do nothing
	if(NULL == GlobalDirect3D9Device)
        return false;

	if(SUCCEEDED(GlobalDirect3D9Device->BeginScene()))
    {
		// Assume that the list is already sorted in ascending order so
		// simply render each of the entities in the vector
		for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
		{
			// If the entity is valid then render it, otherwise intialise it and then render it
			if((*walker)->IsValid())
			{
				// Render the entity
				(*walker)->RenderScreenEntity();
			}
		}
		// End the scene
		GlobalDirect3D9Device->EndScene();
	}

	// Present the back-buffer
	GlobalDirect3D9Device->Present(NULL, NULL, NULL, NULL);
	
	// return ok
	return S_OK;
}

void ExplanationScreen::NextScreen()
{
	// Loop through all entities, stop the animations
	for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
	{
		// If this is an animation and it is running stop it
		if(((*walker)->GetFlags() & SCREENENTITY_ANIMATION) > 0)
		{
			if(((*walker)->GetFlags() & ANIMATION_RUNNING) > 0)
			{
				(*walker)->ControlEntity(false);
			}
		}
	}
}

void ExplanationScreen::StartScreen()
{
	// Make sure that all entities are initialised and ready to be used
	this->Initialise();

	// Loop through the screen entity list and start any CREATE_RUNNING animations
	for(vector<ScreenEntityBase*>::iterator walker = ScreenEntityList.begin();walker != ScreenEntityList.end();walker++)
	{
		// If this is an animation and it is ANIMATION_CREATERUNNING then start it
		if(((*walker)->GetFlags() & ANIMATION_CREATERUNNING) > 0)
		{
			(*walker)->ControlEntity(true);
			(*walker)->SetCurrentAlpha(MAX_ALPHA);
		}
	}

	// Find the original background and turn the alpha to max
	if(FindEntity("Background"))
	{
		static_cast<ScreenEntityStaticImage*>(FindEntity(string("Background")))->SetCurrentAlpha(MAX_ALPHA);
	}

	// Find the unlit background and turn the alpha to minimum
	if(FindEntity("Unlit Background"))
	{
		static_cast<ScreenEntityStaticImage*>(FindEntity(string("Unlit Background")))->SetCurrentAlpha(MIN_ALPHA);
	}
}


the h file:
#ifndef EXPLANATIONSCREEN_H
#define EXPLANATIONSCREEN_H

#include "Screen.h"

class ExplanationScreen : public Screen
{
public:
	//(De)Constructors
	ExplanationScreen();
//	ExplanationScreen(ExplanantionScreen & rhs);
//	~ExplanationScreen();

	// Over-ridden operators
	ExplanationScreen& ExplanationScreen::operator=(ExplanationScreen& rhs);

	// Public member functions
	HRESULT HandleMessage(UINT MessageType, WPARAM wparam, LPARAM lparam);
	HRESULT Initialise();
	void OutputScreenInformation();
	HRESULT RenderScreen();
	void NextScreen();
	void StartScreen();

private:
	HRESULT HandleMouseEvent(UINT MessageType, WPARAM wparam, LPARAM lparam);
	HRESULT HandleGraphEvent(UINT MessageType, WPARAM wparam, LPARAM lparam);
	HRESULT HandleInputEntity(string InputDescriptor);
};
#endif


I have inheritance like this elsewhere in my program and I thought I had followed the implementation closely but I get the following linker errors when I try to compile:
--------------------Configuration: Animation Entity - Win32 Debug--------------------
Compiling...
ExplanationScreen.cpp
Main.cpp
Linking...
LINK : warning LNK4098: defaultlib "LIBCMTD" conflicts with use of other libs; use /NODEFAULTLIB:library
Interface.obj : error LNK2001: unresolved external symbol "public: long __thiscall Screen::HandleMessage(unsigned int,unsigned int,long)" (?HandleMessage@Screen@@QAEJIIJ@Z)
Interface.obj : error LNK2001: unresolved external symbol "public: void __thiscall Screen::NextScreen(void)" (?NextScreen@Screen@@QAEXXZ)
Screen.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall Screen::StartScreen(void)" (?StartScreen@Screen@@UAEXXZ)
Debug/Animation Entity.exe : fatal error LNK1120: 3 unresolved externals
Error executing link.exe.
Creating browse info file...

Animation Entity.exe - 4 error(s), 1 warning(s)

This is probably something simple which I am missing but I can't understand what I am doing wrong, I have declared the methods as virtual in the base class?! Thanks for any help. Mark Coleman

Share this post


Link to post
Share on other sites
It would be interesting to see main.cpp also, cause I can't see anything wrong straight away...one thing though is that Screen::StartScreen() should maybe be declared pure virtual because it's missing in Screen.cpp. To me the linker errors suggests that you somehow are trying to instanciate a Screen object instead of ExplanationScreen although that shouldn't slip through the compiler...umm

Posting where you're creating/storing the screen maybe helps...

Another thing. In your dump there's only a compilation of Expl. and main so a clean/rebuild is always worth a try...you've prob already done so but anyways...

Share this post


Link to post
Share on other sites
// Copy constructor
Screen::Screen(Screen& rhs)
{
// Check for self assignment
if(this == &rhs)
return;


Herb Sutter refers to this as a code joke. The copy constructor is a constructor. That means it constructs objects. Therefore it's impossible for the passed parameter to be the current object because until the copy constructor exits the object does not exist yet.

Easy mistake to make, but quite funny when you realise what you're actually saying.

Enigma

Share this post


Link to post
Share on other sites
Good call Enigma! I normally just create the assignment operator and then copy and paste into the copy constructor, and make the necessary changes. This has been 'slipping through the net' for 3-4 years now!

Mark Coleman

Share this post


Link to post
Share on other sites
Quote:
Original post by mrmrcoleman
Good call Enigma! I normally just create the assignment operator and then copy and paste into the copy constructor, and make the necessary changes. This has been 'slipping through the net' for 3-4 years now!

Mark Coleman


Don't copy and paste. It makes the baby Zahlman cry.

Instead, create the copy constructor, then use the copy-and-swap idiom" to create the assignment operator, avoiding the need to copy the implementation code (you *call* a copy constructor), and also avoiding the need to guard against self-assignment (it would optimize that case to do so, but that shouldn't be the common case anyway).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this