Alternative to Inner Classes in C++ - c++

Let's say I am writing a "Device Tree Blob" for the bcm2835 RPi chip but in C++ files, rather then .dts files. The intent is to practice C++ and OS concepts.
I would like to be able to encapsulate not just register addresses, but functions which access those, and expose only top level uses as API functions.
In C++ this could be inner classes, to one big BCM2835 class like so:
//bcm2835.h
class BMC2835 : public ARMCpu
{
public:
void ACKLedOn(void);
void ACKLdOff(void);
void ACKLedBlink(void);
// I2C write to device (this would be called by the device driver)
// It would ensure that I2C is setup, etc, etc
void I2C_Device_Write(I2C_Device* device, uint8_t* buffer);
private:
// Physical addresses for various peripheral register sets
/// Base Physical Address of the BCM 2835 peripheral registers
const uint32_t BCM2835_PERI_BASE = 0x20000000;
class GPIO()
{
private:
/// Base Physical Address of the Pads registers
const uint32_t BCM2835_GPIO_PADS = (BCM2835_PERI_BASE + 0x100000)
/// Sets the Function Select register for the given pin, which configures
/// the pin as Input, Output or one of the 6 alternate functions.
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode);
}
class I2C()
{
private:
const uint32_t BCM2835_CORE_CLK_HZ = 250000000 ;///< 250 MHz
// Register masks for BSC_C
const uint32_t BCM2835_BSC_C_I2CEN = 0x00008000;///< I2C Enable, 0 = disabled, 1 = enabled
const uint32_t BCM2835_BSC_C_INTR = 0x00000400;///< Interrupt on RX
const uint32_t BCM2835_BSC_C_INTT = 0x00000200;///< Interrupt on TX
void bcm2835_i2c_begin(void);
void bcm2835_i2c_write(uint8_t address, uint8* pbuffer);
}
}
And then I can also have a class for the BCM2837 which is 64-bit and handles the LED very differently for example.
//bcm2837.h
class BCM2837 : public ARMCpu
{
public:
// LED is now a very different Implementation with Mailbox
// but exposed to Kernel as API
void ACKLedOn(void);
void ACKLdOff(void);
void ACKLedBlink(void);
...
...
}
I am sure there many problems with this approach. The one that seems to bother me the most is the length of the single class after you include things like SPI, UART, etc, etc.
Even if the ARMCpu is well desigend and 100% virtual (which I would rather avoid in embedded), each CPU class will still be rather lengthy and difficult to read and maintain.
Is there a way to achieve this type of private level access in C++ which is easier?

Put each chip in its own .cpp file, and declare all those private, internal things within that file (and not in the header). You can wrap them in anonymous namespace to keep them from being exposed to the linker.

Related

Pass pointer of a non static member function to another non static member object

NOTE: In this context, the term "ISR" is not really addressing ISR vectors. There are a lot of tricks in the drivers. One of them is handling with the interrupts generated by the radio tranceiver chip. SX1276_LoRaRadio driver has a thread that checks the actual interrupt source. This is because all these things are working on an rtos (mbed, based on RTX5) and the rtos does not allow direct access to the MCU interrupt vectors. So once PIN_X of the mcu is triggered, the thread reads the radio chip registers for the actual interrupt source.
I am developing for an ARM MCU. There is a radio tranceiver chip (sx1276) on my board. The radio chip driver requires pointers to my own ISR member functions. Thus, I have to pass the pointers of the ISR member functions to another member object (a struct that keeps the pointers) of the same class. The detailed explanation is below.
A low level radio driver handles with the device registers (SX1276_LoRaRadio.cpp). This is provided by the manufacturer and developed exactly for the framework I am using (mbed os). This inherits from LoRaRadio
An API developed by the framwork developer (arm) provides a higher level pure virtual class for abstraction (LoRaRadio.h).
My own radio comm stack exploits the other two in the application layer (ProtoTelecom.hpp and ProtoTelecom.cpp).
The radio has some interrupt pins which are triggered in the occurance of some events. But i can use only one of them due to how the manufacturer designed the driver. This is explained at the very beginning. The events are members of a struct:
// This is in LoRaRadio.h
typedef struct radio_events {
/**
* Callback when Transmission is done.
*/
mbed::Callback<void()> tx_done;
<other mbed::Callback type members>
} radio_events_t;
My ProtoTelecom.hpp looks like this:
class ProtoTelecom: public manageCommands {
public:
explicit ProtoTelecom(LoRaRadio *Radio, tcm_Manager *tcm);
static void OnTxDone(void);
void nonstaticTxDone(void);
<other corresponding ISR member functions>
private:
static radio_events_t RadioEvents;
<other private stuff here>
};
Then in ProtoTelecom.cpp I assign the pointers of the corresponding static member functions to the RadioEvents members:
radio_events_t ProtoTelecom::RadioEvents = {
.tx_done = ProtoTelecom::OnTxDone,
<other pointer assignments>
};
ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
<other stuff goes here>
Radio->init_radio(&RadioEvents);
}
My problem is I have to have multiple instances of ProtoRadio. However, the radio isr functions must be static because passing member function pointers inside the class itself is not allowed in c++. This simply prevents me to initialize another hw because both execute the same isr functions. So how can I pass the pointer of a non static member function to another non static member object inside the class itself? Something like this:
ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
<other stuff goes here>
RadioEvents.tx_done = nonstaticTxDone;
Radio->init_radio(&RadioEvents);
}
I found this thread but the case is slightly different than mine and I could not manage to adapt it to my situation. The main theme of my question is the title but I know that what I am trying to achive is a bit strange and requires some tricks. So if there are other ways to achive my final goal that is also okay for me. Thanks.
As #Wutz stated in his third comment, the correct way is using mbed::Callback. Here is my new constructor:
ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
this->Radio = Radio;
this->tcm = tcm;
//Inizialize the radio
newRadioEvents.tx_done = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnTxDone);
newRadioEvents.tx_timeout = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnTxTimeout);
newRadioEvents.rx_done = mbed::Callback<void(const uint8_t *, uint16_t , int16_t , int8_t)>(this, &ProtoTelecom::nonstaticOnRxDone);
newRadioEvents.rx_timeout = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnRxTimeout);
newRadioEvents.rx_error = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnRxError);
Radio->init_radio( &newRadioEvents );
switchModem(radio_mode);
setState(RADIO_RECEIVE);
}
The newRadioEvents is a non static instance of radio_events_t.

Designing class for handling multiple communication protocols handling

I am developing a C++ application which should handle multiple communication protocols (Ethernet, Serial, etc.). Each of the communication protocols is handled as a specific class.
In order to expose as little as possible information about the internal structure and organization of the said classes and protocols, I would like to somehow wrap all of this functionality and provide somewhat generic API for sending data over a selected protocol.
Basically, what API should provide (the parameters are not restricted to this, but is the general idea):
bool sendData(uint8_t* buffer, const size_t& bufferSize);
void receiveData(uint8_t* dataBuffer, size_t& bufferSize);
What is the best way to create a generic API for the said functionality, and if possible involve some design pattern?
Regards.
What is the best way to create a generic API for the said
functionality, and if possible involve some design pattern?
The Strategy Pattern looks suitable in this scenario.
First, define an interface for all your distinct communication startegies, Communication:
class Communication {
public:
virtual ~CommunicationStrategy() = default;
virtual bool sendData(uint8_t* buffer, const size_t& bufferSize) = 0;
virtual void receiveData(uint8_t* dataBuffer, size_t& bufferSize) = 0;
};
Then, your concrete implementations – i.e., strategies – should derive from this interface:
class EthernetCommunication: public Communication {
public:
// ...
bool sendData(uint8_t*, const size_t&) override;
void receiveData(uint8_t*, size_t&) override;
};
class SerialCommunication: public Communication {
public:
// ...
bool sendData(uint8_t*, const size_t&) override;
void receiveData(uint8_t*, size_t&) override;
};
class CarrierPigeon: public Communication {
public:
// ...
bool sendData(uint8_t*, const size_t&) override;
void receiveData(uint8_t*, size_t&) override;
};
The client code will work with a (pointer to) Communication – i.e., the interface – rather than directly with a particular implementation like EthernetCommunication, SerialCommunication, or CarrierPigeon. Thus, the code follows the "program to an interface, not to an implementation" advice. For example, you may have a factory function like:
std::unique_ptr<Communication> CreateCommunication();
This factory function returns one of the strategies above. Which strategy to return can be determined at run time.
std::unique_ptr<Communication> com = CreateCommunication();
// send data regardless of a particular communication strategy
com->sendData(buffer, bufferSize);
This way, the code above isn't coupled to any particular implementation, but only to the interface Communication which is common to all the different possible communication strategies.
If the different communication strategies don't need per-instance data, just having two callbacks instead of an object will do:
using data_sender_t = bool (*)(uint8_t*, const size_t&);
using data_receiver_t = void (*)(uint8_t*, size_t&);
// set these function pointers to the strategy to use
data_sender_t data_sender;
data_receiver_t data_receiver;

AVR USART: Make data register flexible

I have a library which can write to a serial port on an arduino. It uses an assignment of the form
#define UDRn UDR0
....
void write(uint8_t data) {
UDRn = data;
}
for writing. As far as I understand, UDR0 is the data register for serial port 0. The #define means UDRn will be this data register. The library has a flag to change UDRn to be UDR1 at compile time.
However, I would like to use the same library to write different data to Serial0 and Serial1. So I need to be able to have one instance which writes to UDR0, and one which writes to UDR1. Is it possible to unify this?
Something like:
class Writer {
public:
write(uint8_t);
private:
register target;
}
void Writer::write(uint8_t data) {
target = data;
}
E.g. Can I have pointers to registers?

Specifying device specific functions in struct

I am working with firmware, trying to make a device-independent library. The library uses I2C to communicate, a simple and common protocol for those who don't know what it is. I2C uses two functions that I"m concerned with, read and write. Using I2C on different microcontrollers and such is different for each core mainly and I'm looking for a way for the library to be passed the functions so it can use them in a consistent manner.
How can I make a struct that a user can set variables to functions.
Something like:
typedef struct I2C_setup{
function read = device specific read function;
function write = device specific write function;
}
and then I could call
I2C_setup I2C;
And thereafter
(return type) RegisterRead = I2C.read(register address);
Would I use function pointers or...what?
Function pointer is a good idea. You can defined your prototype as
typedef struct I2C_setup{
(*I2C_Read_Func)(uint32_t devAddr, uint32_t subAddr, uint8_t *pData, size_t lenght);
}
your actual function as
I2C_Read(uint32_t devAddr, uint32_t subAddr, uint8_t *pData, size_t lenght);
and you can assign it by
I2C_setup I2C;
I2C.I2C_Read_Func = I2C_Read;

Inheritance hierarchy vs. multiple inheritance (C++)

Well, I was thinking about a design decision for the past few days and since I still cannot favor one over the other I thought maybe someone else has an idea.
The situation is the following: I have a couple of different interface classes abstracting several communication devices. Since those devices differ in their nature they also differ in the interface and thus are not really related. Lets call them IFooDevice and IBarDevice. More device types may be added over time. The language is C++.
Since other components (called clients from now on) might want to use one or more of those devices, I decided to provide a DeviceManager class to handle access to all available devices at runtime. Since the number of device types might increase, I would like to treat all devices equally (from the managers point of view). However, clients will request a certain device type (or devices based on some properties).
I thought of two possible solutions:
The first would be some kind of interitance hierarchy. All devices would subclass a common interface IDevice which would provide the (virtual) methods necessary for management and device query (like getProperties(), hasProperties(), ...). The DeviceManager then has a collection of pointers to IDevice and at some point a cast from Base to Derived would be necessary - either with a template method in the manager or after the request on the client's side.
From a design point of view, I think it would be more elegant to seperate the concerns of managing a device and the interface of the specific device itself. Thus it would lead to two unrelated interfaces: IManagedDevice and e.g. IFooDevice. A real device would need to inherit from both in order to "be" of a specific device type and to be managaeble. The manager would only manage pointers to IManagedDevice. However, at some point there will be the need to cast between now unrelated classes (e.g. from IManagedDevice to IFooDevice) if a client wants to use a device provided by the manager.
Do I have to choose the lesser of two evils here? And if so which one would it be? Or do I miss something?
Edit:
About the "managing" part. The idea is to have library providing a variety of communication devices different (client) applications can use and share. Managing merely comes down to the storage of instances, methods for registering a new device and looking up a certain device. The responsibility for choosing the "right" device for the task is up to the client side because it knows best which requirements it puts on the communication. In order to reuse and thus share available devices (and by that I mean real instances and not just classes) I need a central access point to all available devices. I'm not too fond of the manager itself but it's the only thing I could come up to in that case.
I think the visitor pattern is a better choice for this.
I think what Tom suggested might be altered a bit to suit your needs:
class IManagedDevice
{
IDevice* myDevice;
/* Functions for managing devices... */
};
In this case IDevice is an empty interface that all devices inherit from. It gives no real benefit, just make the class hierarchy handling slightly more bearable.
Then, you can have then ask for the specific device (IFooDevice or IBarDevice), probably via some sort of device type ID.
If all you need is to have a common code to manage the devices, and then pass each device to the appropriate place I think you can get away with something like this:
class IDevice
{
virtual void Handle() = 0;
};
class IFooDevice : public IDevice
{
virtual void Handle()
{
this->doFoo();
}
virtual void doFoo() = 0;
}
class IBarDevice : public IDevice
{
virtual void Handle()
{
this->doBar();
}
virtual void doBar() = 0;
}
With the manager calling the Handle function.
I think I'd go for a simple solution of having a base class for Device that takes care of registering the device in the global device list and then static methods for looking them up. Something like:
struct Device
{
static Device *first; // Pointer to first available device
Device *prev, *next; // Links for the doubly-linked list of devices
Device() : prev(0), next(first)
{
if (next) next->prev = this;
first = this;
}
virtual ~Device()
{
if (next) next->prev = prev;
if (prev) prev->next = next; else first = next;
}
private:
// Taboo - the following are not implemented
Device(const Device&);
Device& operator=(const Device&);
};
Then you can just derive all devices from Device and them will be automatically placed in the global list on construction and removed from the global list on destruction.
All your clients will be able to visit the list of all devices by starting from Device::first and following device->next. By doing a dynamic_cast<NeededDeviceType*>(device) clients can check if the device is compatible with what they need.
Of course any method that is implemented in every device type (e.g. a description string, a locking method to ensure exclusive use by one client and the like) can be exported also at the Device level.
when communicating with devices I separated the the device and the communication manager completely.
I had a simple communication manager that was based on Boost.Asio. The interface was something like
/** An interface to basic communication with a decive.*/
class coms_manager
{
public:
virtual
~coms_manager();
/** Send a command. */
virtual
void
send(const std::string& cmd) = 0;
/** Receive a command.
* #param buffsize The number of bytes to receive.
* #param size_exactly True if exactly buffsize bytes are to be received. If false, then fewer bytes may be received.
*/
virtual
std::string
recv( const unsigned long& buffsize = 128,
const bool& size_exactly = false) = 0 ;
/** Timed receive command.
* #param buffsize The number of bytes to receive.
* #param seconds The number of seconds in the timeout.
* #param size_exactly True if exactly buffsize bytes are to be received. If false, then fewer bytes may be received.
*/
virtual
std::string
timed_recv( const unsigned long& buffsize = 128,
const double& seconds = 5,
const bool& size_exactly = false) = 0;
};
I then implemented this interface for tcp (ethernet) and serial communications.
class serial_manager : public coms_manager {};
class ethernet_manager : public coms_manager {};
Each of the devices then contained (or pointed to) (rather than inherited) a coms_manager object
For example:
class Oscilloscope
{
void send(const std::string& cmd)
{
m_ComsPtr->send(cmd);
}
private:
coms_manager* m_ComsPtr;
};
You can then swap around the communication method by changing what the pointer points to.
For me, this didn't make much sense (the Oscilloscope was EITHER attached via serial OR via ethernet and so I actually opted for
template<class Manager>
class Oscilloscope
{
void send(const std::string& cmd)
{
m_Coms.send(cmd);
}
private:
Manager m_Coms;
};
and I now use
Oscilloscope<serial_manager> O1(/dev/tty1); // the serial port
Oscilloscope<ethernet_manager> O2(10.0.0.10); //The ip address
which makes more sense.
As for your suggestion as to have a generic device interface. I started with that too, but then wasn't sure of its utility - I always wanted to know exactly what equipment I was sending a command to, I neither needed nor wanted to work through an abstract interface.
At a first glance, the first approach seems fine for me if all devices need to be managed and no other stuff can be done with an unknown device. The meta data for a general device (e.g. name, ...) would typically be the data one need for managing devices.
However, if you need to separate the interface between the management and the device functionality, you can use virtual inheritance.
IManagedDevice and IFooDevice are both interfaces of the same concrete device, so they both have a common virtual base IDevice.
Concretely (run code):
#include <cassert>
class IDevice {
public:
// must be polymorphic, a virtual destructor is a good idea
virtual ~IDevice() {}
};
class IManagedDevice : public virtual IDevice {
// management stuff
};
class IFooDevice : public virtual IDevice {
// foo stuff
};
class ConcreteDevice : public IFooDevice, public IManagedDevice {
// implementation stuff
};
int main() {
ConcreteDevice device;
IManagedDevice* managed_device = &device;
IFooDevice* foo_device = dynamic_cast<IFooDevice*>(managed_device);
assert(foo_device);
return 0;
}