Go Down

Topic: C++ problem with pure virtual method (Read 8182 times) previous topic - next topic

CosineKitty

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?

Code: [Select]

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

*/

CosineKitty

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

Code: [Select]

   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:

Code: [Select]

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

mellis

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

Kiril

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

Coding Badly

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

Code: [Select]

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

Kiril

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

Coding Badly

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

Code: [Select]

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

Kiril

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

Coding Badly


Go Up