I'm trying to implement a hardware abstraction layer (HAL) to access several similar devices using all the same interface.
The question is, how should I implement the dynamic switching of the implementation for one device class vs. another.
The following code shows how I do that actually.
However, I am not sure if this implementation is safe enough to stay. Especially I'm unsure if it is the right way to use 'shared_ptr' for switching the implementation or if it would be better to use a factory pattern or even more abstraction.
What would you recommend to implement such a dynamic HAL?
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// Common HAL base class
struct Base {
virtual string Identify() {return string("Baseclass");}
Base() {cout << "Base Constructor" << '\n';}
virtual ~Base() {cout << "Base Destructor" << '\n';}
// Important to make destructor virtual so that destructor of derived classes is invoked, too
};
// HAL Device 1 Implementation
struct Derived1: public Base {
string Identify() override {return string("Derived 1 class");}
Derived1() {cout << "Derived1 Constructor" << '\n';}
~Derived1() {cout << "Derived1 Destructor" << '\n';}
};
// HAL Device 2 Implementation
struct Derived2: public Base {
string Identify() override {return string("Derived 2 class");}
Derived2() {cout << "Derived2 Constructor" << '\n';}
~Derived2() {cout << "Derived2 Destructor" << '\n';}
};
// Switches implementation via setting a pointer to a class instance
struct ImplementationDispatcher {
shared_ptr<Base> Impl = make_shared<Base>();
Base& Implementation() {return *Impl;}
void SetImplementation(shared_ptr<Base> impl) {Impl = impl;}
};
// Top level class which calls HAL device functions
struct Actor {
ImplementationDispatcher HAL;
void Work() {cout << HAL.Implementation().Identify() << '\n';}
void SetActor(shared_ptr<Base> newImpl) {HAL.SetImplementation(newImpl);}
};
int main()
{
Actor actor;
actor.Work();
actor.SetActor(make_shared<Derived1>());
actor.Work();
actor.SetActor(make_shared<Derived2>());
actor.Work();
}
I define a base class with common functionality for all devices (In the case of this example this is only the Identify()
method).
Then i derive classes for device implementations Derived1
and Derived2
.
I want to switch between those implementations in the main code (e.g. device selector or sth similar. In the example this is just done by calling a setter from the main).
To implement the switching i added a class ImplementationDispatcher
which holds a shared_ptr
to the current implementation for isolation purposes, similar to the pimpl-idiom.
The switching is done by feeding the switcher class with new instances of subclasses from base.
Seems like overengineering. Simply implement your abstract device and pass a proper instance to Work
. At least in your example, there is simply no need for the ImplementationDispatcher
.