Question

I need to create an MVC architecture, where both the View and the Controller classes have already been written as templates as follows:

template<class Model, class View>
class Controller { /* Implement Controller */ };

template<class Model, class Controller>
class View { /* Implement View */ };

I also have a model class:

class Model { /* Implement Model */ };

I have no control over how the Model, View, and Controller classes have been created. Now, how do I instantiate the controller, or the view?

To better explain the situation, if I want to create a Controller using the View and Model classes above (I am NOT allowed to use any other classes), I get:

NOTE: Invalid C++ code follows

Controller< Model, View< Model, // ad infinitum

whereas the following is also invalid:

NOTE: Invalid C++ code follows

Controller< Model, View< Model, Controller > > c;
Was it helpful?

Solution

This is a possible implementation

template<typename View, typename Model>
struct Controller {
    View *view;
    Model *model;

    Controller() : view(0), model(0) {}
    void setUp(View *v, Model *m) {
        view = v;
        model = m;
    }

    virtual void change() = 0;
};

template<typename Controller, typename Model>
struct View {
    Controller *controller;
    Model *model;

    View() : controller(0), model(0) {}
    void setUp(Controller *c, Model *m) {
        controller = &c;
        model = &m;
    }

    virtual void display() = 0;
};

To do the instantiation the trick is deriving a class from a template that has a forward declared class as parameter:

struct MyModel {
    int x;
    MyModel(int x) : x(x) {}
};

struct MyController;
struct MyView : View<MyController, MyModel>
{
    void display() { std::cout << model->x << std::endl; }
};

struct MyController : Controller<MyView, MyModel>
{
    void change() { model->x = 44; }
};

After that you can create instances and set them up

int main(int argc, const char *argv[]) {
    MyModel m(42);
    MyView v;
    MyController c;
    v.setUp(&c, &m); c.setUp(&v, &m);

    v.display();
    c.change();
    v.display();

    return 0;
}

OTHER TIPS

This does compile:

class MyView;
class MyController: public Controller<Model,MyView>{};
class MyView: public View<Model,MyController>{};
MyController myC;
MyView myV;

But it depends on the way Controller and/or View are written. MyView is incomplete at the time MyController is derived, so only pointers can be used.

You can not do this. Some thoughts to get you going:

  • Use templates only for specific member functions

    If you classes do not need to hold data members you can write:

    template<class Model>
    class Controller {
    public:
        void foo() // does not need View
        { ... }
    
        template<typename View>
        void bar(const View& v) // works with a specific View
        { ... }
    };    
    
  • Perhaps Controller does not need to know View?

    template<class Model> // no dependency on View
    class Controller { /* Implement Controller */ };   
    template<class Model, class Controller>
    class View { /* Implement View */ };
    // no it works
    Controller<MyModel> ctrl;
    View<MyModel, Controller<MyModel>> view;
    
  • Use an interface:

    class IController { ... };
    class IView { ... };
    template<class Model>
    class Controller : public IController { /* Implement Controller */ };   
    template<class Model>
    class View : public IView { /* Implement View */ };
    
  • Derive types from your template types:

    class MyView;
    class MyController : public Controller<MyModel,MyView> { };
    class MyView : public View<MyModel,MyController> { };
    

    Here you have to mind this issue: Two template classes being composed of a member of each other

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top