lundi 23 octobre 2023

Abstracting and connecting 2 external libraries

I'm writing the UI layer for my embedded C++ project. For graphic manipulation, I wanted to use the Adafruit_GFX library. I've created an interface

class IRenderer {
public:
    virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
    virtual void drawCircle(
            int16_t x0,
            int16_t y0,
            int16_t r,
            uint16_t color
        ) = 0;
    };
    ...

and connect my interface via bridge with actual library:

class AdafruitRendererBridge : public IRenderer {
private:
    Adafruit_GFX &gfx;

public:
    virtual void drawPixel(uint16_t x, uint16_t y, uint16_t color) {
        gfx.drawPixel(x, y, color);
    }

    ...

    AdafruitRendererBridge(Adafruit_GFX &_gfx) : gfx(_gfx) {}
};

That way, I'm making my code independent of the external library API. Also, I'm making my code testable, as now I can create mock implementing this interface and run unit tests on my host machine without the actual hardware environment that this library needs. Now I'd like to use another external library, ArduinoMenu, that would create a UI for me using provided renderer. The problem is that this library needs a concrete object of the Arduino_GFX type. I've abstracted it and just used my interface. What would be the solution here to initialize ArduinoMenu while using my interface?

I think using template here is an option; however, I'd like to get an explicitly defined set of methods that need to be present, like interfaces do.

Other option is to create some kind of revered bridge:

class RendererAdafruitBridge : public Adafruit_GFX {
private:
    IRenderer &renderer;

public:
    virtual void drawPixel(int16_t x, int16_t y, uint16_t color) override {
        renderer.drawPixel(x, y, color);
    }
   
    ...

    RendererAdafruitBridge(IRenderer &_renderer) : renderer(_renderer) {}
};

However, not every method in Adafruit_GFX that I would need is marked as virtual. Also, there are a lot of methods in Adafruit_GFX and I would need to list them all twice. First in IRenderer and now in this reversed bridge kind of thing.

Aucun commentaire:

Enregistrer un commentaire