3

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;
v vv cvvcv
  • 71
  • 1
  • 2
  • 7
  • 2
    How do you instantiate *any* C++ class into an instance ? You may want to rethink what you're asking, because its about as nebulous as it can be as-is (and the only things that is remotely valid C++ is `Model`; both the controller and view templates are missing the template parameters, which I'm pretty sure is going to be relevant to this). – WhozCraig Apr 06 '14 at 07:33
  • 1
    Also, what's the thing with "self-reference"? – blackbird Apr 06 '14 at 07:35
  • Could you provide some real compiling code example? The code you show is not even valid C++. – Jens Apr 06 '14 at 07:36
  • less than's were not escaped @jww thanks – laune Apr 06 '14 at 07:37
  • Sorry, but I fail to see how this is not valid C++ code? Could you explain the problem please? – v vv cvvcv Apr 06 '14 at 08:01
  • @WhozCraig - The reason I asked the question is because I felt it IS nebulous. – v vv cvvcv Apr 06 '14 at 08:04
  • @blackbird - If I want to instantiate the View class above with the Controller class, I run into a circular loop. – v vv cvvcv Apr 06 '14 at 08:04
  • @Jens - What I have typed above compiles for me, what I want to do does not compile, hence the question. – v vv cvvcv Apr 06 '14 at 08:04
  • Ok, so what do you want to do? Could you post the code that does not compile? The classes you posted up there can be instantiated like `Controller c; View v; Model m;`. Don't think this is what you want. – Jens Apr 06 '14 at 08:09
  • @Jens - updated the question with the invalid code. – v vv cvvcv Apr 06 '14 at 08:10
  • I would guess that you have to create your own classes, maybe using the template classes or inheriting from them. Then instantiate your own classes. – Jens Apr 06 '14 at 08:16
  • If I understand this, and this is a *big* if, it appears to me the designers want an inheritance hierarchy that can draw from both templates for generic implementation, but one of them (makes no difference which) *must* be finite; then the latter defined in terms of the former. Ironically, it would make no difference *which* one was chosen. I have to ask. What mad-man put this together without better usage explanation? – WhozCraig Apr 06 '14 at 08:19
  • @WhozCraig - one doesn't have to be a mad man to create this. Just imagine using libraries from two different vendors and making them work together. – v vv cvvcv Apr 06 '14 at 08:23
  • Thats not the same. The libraries would have to be dependent on each other before you even came to the table. And libraries are dependent at usage, not definition as these templates are. It *does* have to be a mad-man. I have a use-case I *think* works with this, but the intuition is anything-but-clear. If I can formalize it I'll post it. – WhozCraig Apr 06 '14 at 08:28

3 Answers3

4

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.

laune
  • 31,114
  • 3
  • 29
  • 42
  • Wow this actually compiles! You can use forward declared classes as template arguments when writing a class definition? That is new knowledge for me! I wrote some dummy functions in Controller and View and called the View function from Controller - it works!!! – v vv cvvcv Apr 06 '14 at 08:34
  • Sorry, I marked the answer by @6502 as the correct answer because it is much more detailed. But what you are saying is correct. I would up-vote you, but unfortunately, I don't have the necessary reps. Many thanks for your help. – v vv cvvcv Apr 06 '14 at 08:44
  • @vvvcvvcv it compiles because there is no object-usage whatsoever in the actual MyController and MyView classes. Things go south quick unless everything is carefully setup as references and/or pointers. – WhozCraig Apr 06 '14 at 08:45
  • @WhozCraig It must be assumed that the designer of this pair knew what 'e was doing. I was cautioning in my answer: only View* in Controller! – laune Apr 06 '14 at 09:14
4

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;
}
6502
  • 112,025
  • 15
  • 165
  • 265
  • Many thanks. This works for me. I would up-vote you, but don't have the necessary rep. – v vv cvvcv Apr 06 '14 at 08:41
  • +1 This is *very* close to what I pieced together, [which I will link here](https://ideone.com/jNzyC3). i didn't go the extra mile for the functional sample, just the setup. Just glad i wasn't the only one that saw something like this. – WhozCraig Apr 06 '14 at 08:43
1

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

Community
  • 1
  • 1
Danvil
  • 22,240
  • 19
  • 65
  • 88