C++ problem with pure virtual method

I am a fairly experienced C++ programmer, but this has me stumped. I am trying to make a generic class for button debouncing, and then derive from that a class specific to an analog touch sensor I made. But when my base class DebouncedButton tries to call a pure virtual method isButtonPressed(), the compiler barks at me like an angry German Shepherd:

o: In function DebouncedButton::update()': undefined reference to __cxa_pure_virtual'

The line of code it does not like is marked with "//!!!!!" below. Am I doing something wrong, or does this compiler not support pure virtual methods?

/*
    touch_sensor.pde  -  Don Cross  -  http://cosinekitty.com
    
    Experiment with interfacing darlington-pair touch sensor to analog input on atmega8.
*/

//-------------------------------------------------------------------------------------------------

class DebouncedButton {
public:
    DebouncedButton (unsigned _debounceTimeInMilliseconds):
        stableButtonState (false),  // assume button starts out released
        debounceTimeInMilliseconds (_debounceTimeInMilliseconds)
    {
    }

    void update()
    {
        // Check current sensor reading...
        bool sample = isButtonPressed();      //!!!!!
        unsigned long now = millis();

        // Is the current state different from the last stable state?
        if (sample == stableButtonState) {
            // might still be bouncing around, so restart debounce timer
            lastBounceTime = now;
        } else {
            unsigned long elapsed = now - lastBounceTime;
            if (elapsed > debounceTimeInMilliseconds) {
                // the state has changed, and appears stable, so toggle the state now...
                stableButtonState = sample;
                if (stableButtonState) {
                    onPress();
                } else {
                    onRelease();
                }
            }
        }
    }

    virtual bool isButtonPressed() = 0;     // derived class defines what it means for noisy button to be pressed
    virtual void onPress() = 0;             // action to take when debouncer says button is pressed
    virtual void onRelease() = 0;           // action to take when debouncer says button is realeased

private:
    bool            stableButtonState;      // false=button released, true=button pressed
    unsigned long   lastBounceTime;
    unsigned        debounceTimeInMilliseconds;
};


//-------------------------------------------------------------------------------------------------


const int TOUCH_THRESHOLD = 500;
class AnalogTouchSensor: public DebouncedButton {
public:
    AnalogTouchSensor (byte _analogInputPin, byte _digitalOutputPin):
        DebouncedButton (10),        // pass debounce time in milliseconds to DebouncedButton class
        analogInputPin (_analogInputPin),
        digitalOutputPin (_digitalOutputPin),
        digitalOutputState (false)
    {
        pinMode (digitalOutputPin, OUTPUT);
    }

    virtual bool isButtonPressed()
    {
        return analogRead(analogInputPin) < TOUCH_THRESHOLD;
    }

    virtual void onPress()
    {
        digitalOutputState = !digitalOutputState;
        digitalWrite (digitalOutputPin, digitalOutputState);
    }

    virtual void onRelease()
    {
        // do nothing
    }

private:
    byte    analogInputPin;
    byte    digitalOutputPin;
    byte    digitalOutputState;
};


//-------------------------------------------------------------------------------------------------


AnalogTouchSensor sensor(5, 13);     // monitor analog pin 5, toggle LED on pin 13


void setup()
{
}

void loop()
{
    sensor.update();
}

//-------------------------------------------------------------------------------------------------

/*
    $Log: touch_sensor.pde,v $
    Revision 1.1  2007/01/01 02:30:14  dcross
    Almost works, but needs debounce.

*/

As a follow-up, I found a workaround: I modified class DebouncedButton and replaced the following lines:

    virtual bool isButtonPressed() = 0;     // derived class defines what it means for noisy button to be pressed
    virtual void onPress() = 0;             // action to take when debouncer says button is pressed
    virtual void onRelease() = 0;           // action to take when debouncer says button is realeased

with these lines:

    virtual bool isButtonPressed()     // derived class defines what it means for noisy button to be pressed
    {
        return false;
    }

    virtual void onPress()             // action to take when debouncer says button is pressed
    {
    }

    virtual void onRelease()           // action to take when debouncer says button is realeased
    {
    }

The compiler no longer complains, and as a bonus, when I upload and run the sketch, it actually works! So I can tell that my derived class methods are being called, not the base class, as is supposed to be the case. Weird...

The C++ compiler for the AVR doesn't implement all of C++, so I can imagine that it doesn't support pure virtual functions.

I am encountering the same error from the linker when trying to compile a sketch agains the Arduino library from SVN (pre 0016 code, i believe). All worked fine for versions up to 0014, but then they changed HardwareSerial ... CosineKitty, do you mind taking a look?

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1240829167

If you're still curious, try defining the pure virtual methods before update...

// ...
    virtual bool isButtonPressed() = 0;     // derived class defines what it means for noisy button to be pressed
    virtual void onPress() = 0;             // action to take when debouncer says button is pressed
    virtual void onRelease() = 0;           // action to take when debouncer says button is realeased

// ...

void update()
// ...

Does that make any difference?

  • Brian

It turns out you have to define __cxa_pure_virtual() for pure virtual functions to work.

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=410870
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207386508

And a long discussion, a good part of which I don't get:
http://www.mail-archive.com/avr-libc-dev@nongnu.org/msg01223.html

That's really odd. I have a Sketch with a pure virtual function...

class TrdApplication
{
public:
  virtual void Run( bool& OkToSleep ) = 0;
};

...and I haven't had any problems.

I prefer declarations and code separated so that might make a difference.

The compiler / linker must think that you've created an instance of DebouncedButton.

Oh well. I'm glad you found a solution.

  • Brian

That's really odd. I have a Sketch with a pure virtual function...

Indeed it is. Are you compiling this with the Arduino IDE? My problem is not the DebounceButton class, but this. Sorry for taking over this thread.

Arduino 0015 IDE.

  • Brian