0

in a C++ program I have graphs to which I'd like to add some objects. Those can be, for example, common "stand-alone" objects like text, lines etc, or more "smart" objects of different types which act differently and can be connected to an external model to read/write its state.

The simplest thing I have in mind is creating a common interface to all objects with virtual functions like Draw() etc, but the objects can be essentially different (just like text box and scroll bar are different and thus have a different interface). On the other hand, If I don't create a common interface, I'll need to dispatch on objects types, which is usually considered bad practice in C++.

All this is supposed to be kept simple, for example creating widgets and custom message queues would be an overkill, but I want to make something easy to support/extend.

I know there are many patterns for GUI, such as MVC, MVP etc, but those are very general and I'm a bit lost, so if you could give me some directions (or even better, a reference to inspire from) that would be helpful! Thanks.

Roman L
  • 3,006
  • 25
  • 37

2 Answers2

1

For flexibility and scalability, you can use interfaces instead of a single base class. For example extend all objects that can be painted from a IDraw interface. If objects can be updated add and implement a IControl interface and so on. This may look first as an overhead but offers you a good scalability.

Edit:

void* Class::GetInterface(const int id)
{
    if (IDraw::GetId() == id)
    {
        return (IDraw*)this;
    }
    else if (IControl::GetId() == id)
    {
        return (IControl*)this;
    }

    return NULL;
}
cprogrammer
  • 5,503
  • 3
  • 36
  • 56
  • The same question - how would I know in this case which interfaces are supported by each object without dynamic type dispatch? – Roman L Mar 22 '11 at 21:07
  • for interfaces you use often, is more efficient to store them in a list to avoid the overhead from dynamic_cast, for the rest can be used with no problem. Anyway, the overhead is relatively small. – cprogrammer Mar 22 '11 at 21:42
  • you can avoid dynamic_cast by using a COM like approach: implement a base interface that have a function named GetInterface and implement this function like this: I have edited the post for better formating – cprogrammer Mar 22 '11 at 21:52
  • Well, what you are saying is right, but actually I know what the different options are, and my problem is to choose a good pattern. By the way, your example looks wrong - imagine what happens if `Class` doesn't implement `IControl`. Also see a related question about COM-style casting I've asked recently to be sure that you are not wasting your time explaining things I already know :) http://stackoverflow.com/questions/4957739/is-static-cast-misused – Roman L Mar 23 '11 at 03:30
  • My implementation is of course for a class that implements IControl and IDraw. If doesn't support both interfaces of is aware about a 3rd it will look different. I didn't specified because this is very classic and I was pointing just the idea as a variant of using dynamic_cast. – cprogrammer Mar 23 '11 at 07:14
  • If the interface was all the same for all the objects, I could equally make a common interface class and inherit all the objects from it. The problem is to distinguish between objects that do/don't support e.g. `IControl`. C-style casts don't look pretty in any case... – Roman L Mar 23 '11 at 13:15
  • You didn't understand the idea. Interfaces are classes that have all members pure virtual. We are using them just to define behavior. Interfaces (IDraw, IControl) will be implemented by an object only if needed. If a GUI object can be controlled will be derived from IControl interface and will implement IControl's functions (that are pure virtual) and GetInterface (which must be in an IUnknown like interface - the mother of all) must be updated (add smth like if IControl::GetId() == id return this). If cannot be controlled .. not. – cprogrammer Mar 23 '11 at 16:50
  • If you want to know at runtime if an object can be controlled, call obj->GetInterface(ID_OBJ_CONTROL) that will return a pointer to a ICOntrol interface trough which you can control the object. – cprogrammer Mar 23 '11 at 16:51
  • That's quite exactly the COM approach (except for small syntactic differences) which requires ugly conversions to/from `void*`. As I told you, I know how it works... – Roman L Mar 24 '11 at 18:16
1

One possibility would be to use multiple inheritance. Define a drawable base class that only defines enough to draw a visible object, and require all your drawable objects to derive from that. They might (often will) derive from other base classes as well, to define other interfaces they support; that one will just ensure that every item can be drawn when needed.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • But then I'll need to somehow guess which interfaces are supported by each object. For example, if the objects are stored in a homogeneous container (e.g. list), I'd need to convert it from the contained type to each interface. – Roman L Mar 22 '11 at 21:04
  • @7vies: If you're going to have a list, then `objects` all need to be the very same type in any case. In this case, you'd probably want a `list` to hold all your drawable objects (or you might want something like `list >` instead). In any case, it needs to be pointer(-like) things, not the objects themselves, and you only want the drawable-derived objects in that collection to start with. – Jerry Coffin Mar 22 '11 at 21:15
  • @Jerry, sure, I meant pointers, but if I only have a list of `drawable*` how would I support other interfaces - by casting? I will then need a `dynamic_cast`, COM's `QueryInterface` or something similar... – Roman L Mar 22 '11 at 21:19
  • That's why I suggested weak_ptr's -- you almost certainly want one collection of drawable objects, and other collections (that might include pointers to the same actual objects) for other purposes. – Jerry Coffin Mar 22 '11 at 21:24
  • Ok, I see what you mean now, I had this collection-per-interface idea too, but thought it was weird :) – Roman L Mar 22 '11 at 21:31
  • @7vies: I don't think it's weird at all -- quite the contrary, what would strike me as a weird would be a collection of that contained a bunch of objects that apparently had nothing in particular in common, nor much (if any) reason to be in the same collection to start with. – Jerry Coffin Mar 22 '11 at 21:45
  • Well, they are all drawable as you said, so they have at least this in common. What I considered weird was having multiple collections pointing to the same objects just to avoid dynamic casts. Probably I was wrong – Roman L Mar 22 '11 at 21:50
  • @7vies: I don't see it as being just to avoid dynamic casts -- it's to keep things organized. – Jerry Coffin Mar 22 '11 at 22:13
  • Assuming all my objects are drawable, I'd say it is already well organized to put all of them in a single container. So for me the organization question is already solved at this point. But then the problem is how to get different interfaces to these objects, i.e. dynamic type dispatch. This can be solved as you propose, by creating a list for each interface, but there are also other options e.g. those I've mentioned. So I'm not sure it's a question of keeping things organized, for me it is more like choosing how to implement interface queries. – Roman L Mar 23 '11 at 13:47