2 years, 4 months ago.

I2C class: inherit another class

Dear sirs,

I have a strange behaviour which I simply do not understand (maybe it's really a matter of understanding...)

I want to drive a LCD-display with I2C interface by a Nucleo. The original idea was to use the I2C class and extend it by a few (LCD) functions.

The headerfile (code snipped) looked like that:

class LCD_I2C : public Stream { public: LCD_I2C(I2C& i2c);

private: uint8_t LCDAdr;

protected: I2C& _i2c; char LCDByte[1];

virtual int _putc(int value); virtual int _getc(); };

The constructor is that: LCD_I2C::LCD_I2C (I2C& i2c) : _i2c(i2c) { LCDAdr = 0; }

And the main code is that:

I2C i2c(I2C_SDA, I2C_SCL); LCD_I2C LCDObj (i2c);

int main ....

This caused the message "Object of abstract class type "LCD_I2C" is not allowed ..." with error code 322.

The online help states that there are possibly virtual functions are not defined. Well, if I activate the "_putc" and "_getc" methods, it works. But why ? I mean the upper level classes (stream, filelike ...) contain several virtual methods but non of them are causing this error message. By the way: _getc and _putc are defined as empty functions in LCD_I2C.cpp and are nowhere called !

What is the background I don't understand ?

Best regards, Peter

1 Answer

2 years, 4 months ago.

The issue you are referencing with _putc() is that it is defined in Stream.h as pure virtual:

virtual int _putc(int c) = 0;

The =0, means you must implement this yourself, there is no implementation in Stream class. This is because there is no way for Stream to know where you want to actually putc the characters.

The only reason for LCD to inherit from Stream is if you really need/want to use the full Stream interface with all the fancy methods (read, write, seek, rewind, printf, etc, etc).

I would be immediately wary of trying to implement the Stream interface. There is a lot going on here and it is probably more complicated than what you need. Stream inherits from both FileLike and NonCopyable which themselves inherit from additional classes. Stream is just an interface and if you want to use it you are going to have to go through and implement the virtual functions for your class so it is compatible with the Stream interface. It is possible all you have to do is implement _putc() and _getc() - I don’t know I’ve never tried. Looks like you also need to provide the char buffer for Stream in the ctor.

I would get started by having a send() method inside class LCD that just accepts a regular char buffer. Easy.

class LcdDisplay{
public:
    LcdDisplay(I2C& i2c) :
        _i2c(i2c),
        _address(0) {
    }
    
    // put string on LCD
    void send(char* buffer) {
        // Do stuff to put buffer on I2C bus
    }

private:
    I2C& _i2c;
    uint8_t _address;
};

Also, I was getting some weird compile errors around naming. Class name LCD_I2C did not work.

Ah, I see the point. Now it becomes clear. I have removed the "public stream" part and now the compiler isn't complaining anymore (even without _putc and so on). I was just using an example (to have a base and makes things easy; stripped the example and tried to get it running) and simply didn't understand where the problem is. By the way I'm writing programs in C, VBA, Pascal and assembler for more than 25 years (just on low level) but I'm not very familiar with C++, so sometimes I'm tripping over basic stuff. :-)

Thnx for the help, Peter

posted by Jack Black 27 May 2019