c++factoryderived-classhalpimpl-idiom

Dynamic switching of implementation in hardware abstraction layer (HAL)


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.


Solution

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