views:

276

answers:

3

I am trying to create a generic menu button class that is templated to a class, so I can make this button in any class. I want to create a void function pointer to different functions in that class, so when you click on the New Game button, it will call the NewGame() function etc.

I'm still a little new to the idea of creating function pointers and would like some advice.

I get a crazy link error everytime I try to compile my code now with this Menubutton.

here is the error:

Error 1 error LNK2019: unresolved external symbol "public: void __thiscall MENUBUTTON::Draw(void)" (?Draw@?$MENUBUTTON@VTitleScreen@@@@QAEXXZ) referenced in function "public: virtual void __thiscall TitleScreen::Draw(void)" (?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 which is using the 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 the buttons dynamic for any case in a different way, or know what this problem is, please help! :)

+1  A: 

To get rid of the link error, move all method definitions starting with template from the .cpp file to the .h file. In your code, move these:

template <class t> void MENUBUTTON<t>::SetButton ... { ... }
template <class t> void MENUBUTTON<t>::Draw ... { ... }

The reason why it doesn't work with the .cpp file is that the compiler treats MENUBUTTON<Foo> and MENUBUTTON<Bar> etc. as different classes, and it generates and compiles such a class whenever it is used. So if you use MENUBUTTON<TitleScreen> when compiling titlescreen.cpp, then the MENUBUTTON<TitleScreen> code will be compiled into the object file titlescreen.o. But the methods SetButton and Draw will be missing at link time, because they are not defined in titlescreen.cpp or any of the .h files it includes. MenuButton.o won't contain it either, because MenuButton.cpp does not need MENUBUTTON<TitleScreen>.

pts
Thanks, I think I understand the templated class in C++ better now too.
A: 

Just to clarify the first answer, you can't have a template class that is declared in a header and then implemented in a separated compiled translation unit (i.e. .cpp file). You've got to put all the code in the header. You can either put it all in the initial class declaration, or using "inline" declare the class first with the functions, then implement the specific functions further down in the header.

Jim Crafton
A: 

Speaking of a better way ... why do you need a template here? Plain old virtual function or pointer to member would do. This would be a perfect application of the Command Design Pattern. Templates give you compile-time polymorphism, while you are probably looking for run-time polymorphism here.

Nikolai N Fetissov
I'll certainly look into this next week before I implement anything.