Tim
Tim

Reputation:

Problems with setting up Function Pointers in Templated Class

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! :)

Upvotes: 0

Views: 657

Answers (3)

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84239

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.

Upvotes: 0

Jim Crafton
Jim Crafton

Reputation: 361

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.

Upvotes: 0

pts
pts

Reputation: 87381

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>.

Upvotes: 1

Related Questions