0

I have written a codebender project which returns this error:

Oops! Looks like there was a serious issue with your project. 

If you are not sure what could be wrong please contact us

/mnt/tmp/compiler.amxHJ9/files/Many Games.o: In function `gamesMaster::gamesMaster()':
Many Games.cpp:(.text._ZN11gamesMasterC2Ev+0xa): undefined reference to `vtable for gamesMaster'
Many Games.cpp:(.text._ZN11gamesMasterC2Ev+0xc): undefined reference to `vtable for gamesMaster' 

What does it mean by vtable?

The code uses basic classes and inheritance.

The program is designed to select and play the game of your choice. I have only programmed the basic structure.

Class games contains a ref to the class gamesMaster.

Class gamesMaster contains a ref to simpleTimer.

Classes playHiLow and playSimon are children of gamesMaster.

Class games handles and input and outputs, win and lose control and keeps track of lives.

Class gamesMaster handles the simpleTimer, displays a new round, checks for the correct answer, and checks if game has finished successfully.

The main() function runs a routine in the games class.

The code is listed here: https://codebender.cc/sketch:144869

class simpleTimer
{
private:
    unsigned long _previousMillis;
    unsigned long _interval;

public:
    simpleTimer()
    {

    }
    void start(float mins)
    {
        _interval = (unsigned long) (mins * 60 * 1000);
        _previousMillis = millis();
    }
    bool stillRunning()
    {
        return (millis() - _previousMillis < _interval);
    }
};

class gamesMaster
{
public:
    unsigned short lives;
    gamesMaster()
    {
        timeOutTimer = new simpleTimer();
        gameTimer = new simpleTimer();
        gameTimer->start(1.5);
        lives = 5;
    }
    void right()
    {
        Serial.println("Correct");
    }
    void wrong()
    {
        Serial.println("Wrong");
    }
    void winGame()
    {
        Serial.println("**********");
        Serial.println("* WINNER *");
        Serial.println("**********");
    }
    void loseGame()
    {
        Serial.println("\\ /          \\ /");
        Serial.println(" X    LOST    X");
        Serial.println("/ \\          / \\");
    }
    void startTimeOut(float mins)
    {
        timeOutTimer->start(mins);
    }
    bool notTimedOut()
    {
        return timeOutTimer->stillRunning();
    }
    bool gameTimerOver()
    {
        return !gameTimer->stillRunning();
    }
    simpleTimer *timeOutTimer;
    simpleTimer *gameTimer;

    virtual void setupForNewRound(void);
    virtual bool checkAnswer(unsigned short);
    virtual bool reachedEnd(void);
};

class gameHiLow: public gamesMaster
{
public:
    gameHiLow() : gamesMaster() {}
    void setupForNewRound()
    {
        Serial.println("  ->(HiLow) New round");
    }
    bool checkAnswer(unsigned short answer)
    {
        Serial.println("  ->(HiLow) Correct Answer");
        return HIGH;
    }
    bool reachedEnd()
    {
        Serial.println("  ->(HiLow) End of game");
        return HIGH;
    }
};

class gameSimon: public gamesMaster
{
public:
    gameSimon() : gamesMaster() {}
    void setupForNewRound()
    {
        Serial.println("  ->(Simon) New round");
    }
    bool checkAnswer(unsigned short answer)
    {
        Serial.println("  ->(Simon) Correct Answer");
        return HIGH;
    }
    bool reachedEnd()
    {
        Serial.println("  ->(Simon) End of game");
        return HIGH;
    }
};


class games
{
private:
    unsigned short _gameToPlay;
    unsigned short _levelToPlay;
    gamesMaster *myGame;
    unsigned short _getAnswer()
    {
        if (Serial.available() > 0)
        {
            int inByte = Serial.read();
            Serial.println("  ->(games) Decode Answer");

            switch (inByte)
            {
                case '<':
                    return 1;
                    break;
                case '>':
                    return 2;
                    break;
            }

        }
        return 0;

    }
    bool _respond()
    {
        unsigned short answer;
        while(myGame->notTimedOut())
        {
            if((answer = _getAnswer()) != 0)
            {
                if(myGame->checkAnswer(answer) == HIGH) return HIGH;
            }
        }
        return LOW;
    }
public:
    games(unsigned short gameToPlay, unsigned short levelToPlay)
    {
        _gameToPlay = gameToPlay;
        _levelToPlay = levelToPlay;
        switch(_gameToPlay)
        {
            case 1:
                myGame = new gameHiLow();
                Serial.println(" ->(game) play Higher or Lower Game");
                break;
            case 2:
                myGame = new gameSimon();
                Serial.println(" ->(game) play Simon Game");
                break;
        }
    }
    void play()
    {
        unsigned short answer;
        while(!myGame->gameTimerOver())
        {
            myGame->setupForNewRound();
            myGame->startTimeOut(.5);
            if(_respond() == HIGH)
            {
                myGame->right();
                if(myGame->reachedEnd())
                {
                    myGame->winGame();
                    return;
                }
            }
            else
            {
                myGame->wrong();
                if(--myGame->lives <= 0)
                {
                    myGame->loseGame();
                    return;
                }
            }
        }
        myGame->loseGame();
    }
};


//////////////////////////////////////////////////////////////////////////////////
games *game;

void setup()
{
    Serial.begin(9600);
    unsigned short gameToPlay = 1;
    unsigned short levelToPlay = 1;
    game = new games(gameToPlay, levelToPlay);
}

void loop()
{
    Serial.println("Let the games begin");
    game->play();
    Serial.println("Program Complete");
    delay(5000);
    exit(0);
}

2 Answers 2

1

I agree with @GeryOshlike. But that's not the only option.

Yes, you can make them pure virtual:

virtual void setupForNewRound(void) = 0;
virtual bool checkAnswer(unsigned short) = 0;
virtual bool reachedEnd(void) = 0;

Or you can make them virtual with a default function:

virtual void setupForNewRound(void) { }
virtual bool checkAnswer(unsigned short) { return false; }
virtual bool reachedEnd(void) { return false; }

Pure virtual will complain when you compile if you haven't implemented those functions in your child class. By providing a default function body it will not complain if you haven't implemented it, and will just use the default body.

If it's a function which you know will always have to be implemented at the child level (will always be different) then go the pure virtual route. If it's a function that will be the same in a number of children but differ in only a few, then providing the default body and overriding with different ones when needed is the better way to go.

0

As discussed here

https://stackoverflow.com/questions/3065154/undefined-reference-to-vtable

declare these functions pure virtual:

virtual void setupForNewRound(void) = 0;
virtual bool checkAnswer(unsigned short) = 0;
virtual bool reachedEnd(void) = 0;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.