I'm writing a real-time operating system for microcontrollers in C++11 - distortos. Currently I'm thinking about a C++ framework for various peripherals. The most basic peripheral which I would like to have there are input/output pins. Because such input/output pins can both be built into the chip or be external (I2C expanders, shift registers, ...) I plan to implement interfaces for them with pure abstract classes.

These are some of mine requirements:

  • some pins can only be inputs and it shouldn't be possible to (easily) change their direction - for example if you change the direction of pin connected to push-button to "output" you can irreversibly damage the chip, the board or the device that is connected to the other side of the wire;
  • some pins can only be outputs and it shouldn't be possible to (easily) change their direction - if you are using a HC595 shift register then this can only be "output" and nothing more;
  • some pins can change their direction freely - this is required if you want to implement software I2C or software 1-wire;

Initially I have thought about doing it like this:

  1. The most basic interface class would be InputPin - it would only support "reading" the state of the pin.
  2. Second interface class would be OutputPin - it would inherit from InputPin (as you can read the value of pin configured as output) and also add the ability to "write" the value.
  3. Third interface class - InputOutputPin would inherit from OutputPin and also provide a function to change direction.

Such approach has a very nice feature that I can use use any derived class in function that requires one of the bases. But actually there is one substitution that is not always correct - using InputOutputPin in a function that requires OutputPin can be a problem when the instance of InputOutputPin is currently configured to be an input...

I've tried googling around for some inspiration and it seems that there is not much to use. ARM's mbed framework has 3 completely unrelated classes (there is no inheritance there) - one for "in", one for "out" and one for "in/out". Most of the code that I found has only one class that is for "in/out". I've actually found an article about exactly the same issue that I'm facing - http://www.embeddedrelated.com/showarticle/108.php . The solution presented there doesn't suit my needs - in a multi-threaded environment the operation of changing the direction of the pin is not so simple and usually requires exclusive access to configuration registers (the change is a read-modify-write operation).

I wouldn't like to implement any solution where the functions could fail (for example return error codes when the operation is not possible) - this would really complicate the usage.

Implementing a type-conversion operator - so that conversion from InputOutputPin to OutputPin would be done in a function which also makes sure the direction is correct - is error prone, because the reference to OutputPin can be stored for later use and at that time the direction may have changed again.

Maybe I should just simplify the hierarchy - OutputPin and InputOutputPin would both inherit from InputPin (this would always be correct), so OutputPin wouldn't be a base for InputOutputPin. This looses some of the flexibility, but I'm not sure that such substitution would be required anyway.

  • How would an InputOutputPin react if the pin is configured for input and you try to write to it? Jan 21 '16 at 11:53
  • @BartvanIngenSchenau - this would be a problem of the user (; but in that case it would be evident - to use InputOutputPin as output you must make sure that the direction is correct. But this wouldn't be so evident when converting InputOutputPin to OutputPin and using it through reference to base class - in that case it can be assumed that the direction is always "output", which is not necessarily true in that case and cannot be ensured... Jan 21 '16 at 14:03

Look at prior art - the Apple IOKit is the C++ framework/API for device drivers in OS X/Darwin kernel. Also, you may even get some inspiration from the Linux kernel - Torvalds is a famous C++ hater, but many drivers (e.g. USB drivers for 4G mobile modems) are using hand-hacked inheritance in C, and the driver code is often quite readable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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