Adam Dohnal
Adam Dohnal

Reputation: 77

C++: Mixins and polymorphism

I am trying to fit Mixin pattern to my problem and I have one problem with polymorphism, which I don't know how to solve effectively. I want to ask you for advice (maybe there is some cool c++ feature I am not aware of) before trying to redesign my program.

I want to present it in very straightforward and easy way, so the use case here might not make sense.

I have simply a Window class

struct WindowCreateInfo {
    std::string title;
    int x, y;
    int width, height;
};

class Window {
public:
    Window(const WindowCreateInfo &createInfo) :
            title(createInfo.title),
            x(createInfo.x),
            y(createInfo.y),
            width(createInfo.width),
            height(createInfo.height) {}

    const std::string &getTitle() const { return title; }

    int getX() const { return x; }

    int getY() const { return y; }

    int getWidth() const { return width; }

    int getHeight() const { return height; }

public:
protected:
    std::string title;
    int x, y;
    int width, height;
};

Then I define two mixins Resizable and Movable as follows

template<class Base>
class Resizable : public Base {
public:
    Resizable(const WindowCreateInfo &createInfo) : Base(createInfo) {}

    void resize(int width, int height) {
        Base::width = width;
        Base::height = height;
    }
};

template<class Base>
class Movable : public Base {
public:
    Movable(const WindowCreateInfo &createInfo) : Base(createInfo) {}

    void move(int x, int y) {
        Base::x = x;
        Base::y = y;
    }
};

Next, I have some business layer where I work with instances of Window

class WindowManager {
public:
    static void resize(Resizable<Window> &window, int width, int height) {
        window.resize(width, height);

        // any other logic like logging, ...
    }

    static void move(Movable<Window> &window, int x, int y) {
        window.move(x, y);

        // any other logic like logging, ...
    }
};

The obvious problem here is that following does not compile

using MyWindow = Movable<Resizable<Window>>;

int main() {
    MyWindow window({"Title", 0, 0, 640, 480});

    WindowManager::resize(window, 800, 600);

    // Non-cost lvalue reference to type Movable<Window> cannot bind
    // to a value of unrelated type Movable<Resizable<Window>>
    WindowManager::move(window, 100, 100);
};

I understand that there is difference between Movable<Window> and Movable<Resizable<Window>> because the latter Movable could use Resizable. In my design, the mixins are independent and the order in which they are mixed in does not matter. I guess this use of mixins is very common.

Is there any way how to make this code compile while keeping the design as much as possible?

Upvotes: 3

Views: 303

Answers (1)

user4442671
user4442671

Reputation:

Is there any way how to make this code compile while keeping the design as much as possible?

You can simply make the window manager accept arbitrary versions of Resizable<> and Movable<> by templating the methods:

class WindowManager {
public:
    template<typename Base>
    static void resize(Resizable<Base> &window, int width, int height) {
        window.resize(width, height);

        // any other logic like logging, ...
    }

    template<typename Base>
    static void move(Movable<Base> &window, int x, int y) {
        window.move(x, y);

        // any other logic like logging, ...
    }
};

Upvotes: 5

Related Questions