Problems with setting function pointers in Templated Class
I'm trying to create a generic menu button class that is a template for the class, so I can make this button in any class. I want to create a void function pointer for different functions in this class, so when you click the New Game button it will call the NewGame () function, etc.
I'm still a little familiar with the idea of creating function pointers and would like some advice.
I get a crazy link error every time I try to compile my code with this Menubutton.
here is the error:
Error 1 error LNK2019: unresolved external symbol "public: void __thiscall MENUBUTTON :: Draw (void)" (? Draw @? $ @ VTitleScreen button MENU @@@@ QAEXXZ) function reference "public: virtual void __thiscall TitleScreen :: Draw (canceled) "(? Draw @TitleScreen @@ UAEXXZ) TitleScreen.obj
MenuButton.h
template <class t>
struct MENUBUTTON
{
SPRITE Normal; // Sprite to display when not hovered over or pressed down
SPRITE Hover; // Sprite to display when hovered over
RECTANGLE HoverBounds; // The Rectangle that activates the hover flag
t* pClass; // Pointer to the templated class
void (t::*ClickFunction)(); // Pointer to void function
void SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)());
bool IsMouseHover();
void CheckPressed();
void Draw();
};
MenuButton.cpp
#include "Global.h"
template <class t>
void MENUBUTTON<t>::SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)())
{
// Position
Hover.position.x = Normal.position.x = xPos;
Hover.position.y = Normal.position.y = yPos;
Hover.position.z = Normal.position.z = 0;
// Width / Height
Hover.width = Normal.width = width;
Hover.height = Normal.height = height;
// Hover RECTANGLE
HoverBounds.x = xPos + hPadLeft;
HoverBounds.y = yPos + hPadTop;
HoverBounds.width = hWidth;
HoverBounds.height = hHeight;
// Load the Sprites
LoadSprite(&Normal, normalFilePath, width, height, 1, 1);
LoadSprite(&Hover, hoverFilePath, width, height, 1, 1);
// Set the Click function pointer
this->pClass = objectClass;
this->ClickFunction = ClickFunction;
}
template <class t>
void MENUBUTTON<t>::Draw()
{
if(IsMouseHover())
{
DrawSprite(&Hover, 0, Hover.position.x, Hover.position.y, Hover.position.z);
}
else
{
DrawSprite(&Normal, 0, Normal.position.x, Normal.position.y, Normal.position.z);
}
}
template <class t>
bool MENUBUTTON<t>::IsMouseHover()
{
return (((InputData.MousePosition.x >= HoverBounds.x) && (InputData.MousePosition.x <= (HoverBounds.x + HoverBounds.width))) &&
((InputData.MousePosition.y >= HoverBounds.y) && (InputData.MousePosition.y <= (HoverBounds.y + HoverBounds.height)))) ? true : false;
}
Here is my title screen that uses a menu button.
TitleScreen.h
class TitleScreen : public BaseState
{
// SPRITES
SPRITE titleScreenBG;
// MENU BUTTONS
MENUBUTTON<TitleScreen> playButton;
MENUBUTTON<TitleScreen> quitButton;
public:
TitleScreen();
virtual void Initialize();
virtual void End();
virtual void Update(float dt, INPUTDATA* input);
virtual void Draw();
void QuitGame();
void NewGame();
};
TitleScreen.cpp
#include "Global.h"
// Constructors
TitleScreen::TitleScreen()
{
}
// Virtual Voids
void TitleScreen::End()
{
}
void TitleScreen::Initialize()
{
this->Enabled = true;
this->Visible = true;
// Initialize sprites
ZeroMemory(&titleScreenBG, sizeof(SPRITE));
LoadSprite(&titleScreenBG, TEXT("../../PNG/TitleScreenBG.png"), 1440, 900, 1, 1);
titleScreenBG.position.x = titleScreenBG.position.y = titleScreenBG.position.z = 0;
// Initialize buttons
ZeroMemory(&playButton, sizeof(MENUBUTTON<TitleScreen>));
playButton.SetButton(55, 170, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
400, 70, // Hover width, Hover height
TEXT("../../PNG/NewGame.png"), TEXT("../../PNG/NewGameH.png"),
this, &TitleScreen::NewGame);
ZeroMemory(&quitButton, sizeof(MENUBUTTON<TitleScreen>));
quitButton.SetButton(55, 240, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
190, 70, // Hover width, Hover height
TEXT("../../PNG/QuitButton.png"), TEXT("../../PNG/QuitButtonH.png"),
this, &TitleScreen::QuitGame);
}
void TitleScreen::Update(float dt, INPUTDATA* input)
{
}
void TitleScreen::Draw()
{
StartRender();
DrawSprite(&titleScreenBG, 0, titleScreenBG.position.x, titleScreenBG.position.y, titleScreenBG.position.z);
playButton.Draw();
quitButton.Draw();
EndRender();
}
// Public Methods
void TitleScreen::QuitGame()
{
CloseDirect3D();
}
void TitleScreen::NewGame()
{
CloseDirect3D();
}
If anyone has any advice on how I can make buttons dynamic for any occasion differently, or know what the problem is, please help! :)
To get rid of the communication error, move all method definitions template
from file .cpp
to file .h
. In your code, move them:
template <class t> void MENUBUTTON<t>::SetButton ... { ... }
template <class t> void MENUBUTTON<t>::Draw ... { ... }
The reason why it does not work with the file .cpp
, is that the compiler treats MENUBUTTON<Foo>
and MENUBUTTON<Bar>
etc. as different classes, and generates and compiles such a class whenever it is used. Therefore, if you use MENUBUTTON<TitleScreen>
when compiling titlescreen.cpp
, then the code MENUBUTTON<TitleScreen>
will be compiled into an object file titlescreen.o
. But the methods SetButton
and Draw
will be absent during the link because they are not defined in titlescreen.cpp
or in any of the files .h
that it includes. MenuButton.o
will also not contain it, because it MenuButton.cpp
does not need MENUBUTTON<TitleScreen>
.
a source to share
To clarify the first answer, you cannot have a template class that is declared in the header and then implemented in a separate compiled translation unit (i.e. cpp file). You have to put all the code in the header. You can either put all of this in the initial class declaration, or use "inline" to first declare the class with functions and then implement specific functions later in the header.
a source to share
Speaking of the best ... why do you need a template here? Plain old virtual function or member pointer. This would be an ideal application of the command design pattern . Templates give you compile-time polymorphism , although you're probably looking for run-time polymorphism here.
a source to share