2

I have run into an annoying problem lately, and I am not satisfied with my own workaround: I have a program that maintains a vector of pointers to a base class, and I am storing there all kind of children object-pointers. Now, each child class has methods of their own, and the main program may or not may call these methods, depending on the type of object (note though that they all heavily use common methods of the base class, so this justify inheritance).

I have found useful to have an "object identifier" to check the class type (and then either call the method or not), which is already not very beautiful, but this is not the main inconvenience. The main inconvenience is that, if I want to actually be able to call a derived class method using the base class pointer (or even just store the pointer in the pointer array), then one need to declare the derived methods as virtual in the base class.

Make sense from the C++ coding point of view.. but this is not practical in my case (from the development point of view), because I am planning to create many different children classes in different files, perhaps made by different people, and I don't want to tweak/maintain the base class each time, to add virtual methods!

How to do this? Essentially, what I am asking (I guess) is how to implement something like Objective-C NSArrays - if you send a message to an object that does not implement the method, well, nothing happens.

regards

MrHug
  • 1,315
  • 10
  • 27
alvaro
  • 35
  • 4
  • 11
    Have you noticed how code on a programming site is better than a loose description of said code? – Mitch Wheat Mar 10 '13 at 08:04
  • 2
    Use paragraphs! It is scary to read this much all in one single paragraph. – Nawaz Mar 10 '13 at 08:04
  • 2
    "how to implement something like Objective-C NSArrays - if you send a message to an object that does not implement the method, well, nothing happens". I really hope that is not possible. I truly do. – Shoe Mar 10 '13 at 08:13
  • 1
    That only shows how great NSArrays must be. Also, why is it *inconvenience* to declare methods `virtual`? – Bartek Banachewicz Mar 10 '13 at 08:16
  • 1
    If you have that disparaging a difference in child-types, then perhaps they don't belong in the same hierarchy to begin with. If they do, then consider expanding your base-contract and providing template-implementations that snubs non-required implementations with *detectable* lack of support (i.e. an E_NOTIMPL condition). I still think, however, you should seriously consider (a) posting code, and (b) rethink your design to better grass-roots. – WhozCraig Mar 10 '13 at 08:18
  • Don't get me wrong: I love C++ clarity (and as I said virtual makes perfect sense) it is just form the development point of view that I am worried. It will be difficult to maintain the base class at one point. – alvaro Mar 10 '13 at 08:20

4 Answers4

1

You can do what you describe in C++, but not using functions. It is, by the way, kind of horrible but I suppose there might be cases in which it's a legitimate approach.

First way of doing this:

Define a function with a signature something like boost::variant parseMessage(std::string, std::vector<boost::variant>); and perhaps a string of convenience functions with common signatures on the base class and include a message lookup table on the base class which takes functors. In each class constructor add its messages to the message table and the parseMessage function then parcels off each message to the right function on the class.

It's ugly and slow but it should work.

Second way of doing this:

Define the virtual functions further down the hierarchy so if you want to add int foo(bar*); you first add a class that defines it as virtual and then ensure every class that wants to define int foo(bar*); inherit from it. You can then use dynamic_cast to ensure that the pointer you are looking at inherits from this class before trying to call int foo(bar*);. Possible these interface adding classes could be pure virtual so they can be mixed in to various points using multiple inheritance, but that may have its own problems.

This is less flexible than the first way and requires the classes that implement a function to be linked to each other. Oh, and it's still ugly.

But mostly I suggest you try and write C++ code like C++ code not Objective-C code.

Jack Aidley
  • 19,439
  • 7
  • 43
  • 70
  • thanks! I had tried the dynamic_cast strategy - but I still need to add (pure virtual methods) and keep track of these virtual methods added by other potential contributors. Your first solution looks intriguing! I will think of it. By the way, I am NOT a software developper - I am now developping an application on an ARM Cortex microcontroller.. and NO, it's not an iphone (an mbed). And also, I am definitely not coding in Objective-C style! It was just a question. It looks like here people react very strongly to breaches in programming paradigms. Interesting – alvaro Mar 10 '13 at 11:15
1

This can be solved by adding some sort of introspection capabilities and meta object system. This talk Metadata and reflection in C++ — Jeff Tucker demonstrates how to do this using c++'s template meta programming.

If you don't want to go to the trouble of implementing one yourself, then it would be easier to use an existing one such as Qt's meta object system. Note that this solution does not work with multiple inheritance due to limitations in the meta object compiler: QObject Multiple Inheritance.

With that installed, you can query for the presence of methods and call them. This is quite tedious to do by hand, so the easiest way to call such a methods is using the signal and slot mechanism.

There is also GObject which is quite simmilar and there are others.

Community
  • 1
  • 1
Thomas
  • 4,980
  • 2
  • 15
  • 30
  • this looks fantastic! thank you for this great links. I will consider this when developing on a computer - I am now developing on an mbed microcontroller (cortex m3), which probably does not have enough memory for using these libraries. (I wish I could vote you, but I am new to stack overflow and the system refuses my vote yet) – alvaro Mar 10 '13 at 11:19
1

Instead of this:

// variant A: declare everything in the base class
void DoStuff_A(Base* b) {
  if (b->TypeId() == DERIVED_1) 
      b->DoDerived1Stuff();
  else if if (b->TypeId() == DERIVED_2) 
      b->DoDerived12Stuff();
}

or this:

// variant B: declare nothing in the base class
void DoStuff_B(Base* b) {
  if (b->TypeId() == DERIVED_1) 
      (dynamic_cast<Derived1*>(b))->DoDerived1Stuff();
  else if if (b->TypeId() == DERIVED_2) 
      (dynamic_cast<Derived2*>(b))->DoDerived12Stuff();
}

do this:

// variant C: declare the right thing in the base class
b->DoStuff();

Note there's a single virtual function in the base per stuff that has to be done.

If you find yourself in a situation where you are more comfortable with variants A or B then with variant C, stop and rethink your design. You are coupling components too tightly and in the end it will backfire.

I am planning to create many different children classes in different files, perhaps made by different people, and I don't want to tweak/maintain the base class each time, to add virtual methods!

You are OK with tweaking DoStuff each time a derived class is added, but tweaking Base is a no-no. May I ask why?

If your design does not fit in either A, B or C pattern, show what you have, for clairvoyance is a rare feat these days.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • I am now doing something very similar to A pattern, and I was trying to get B pattern work. Thanks! now, your point is very good: >You are OK with tweaking DoStuff each time a derived class is added, but tweaking Base is a no-no. May I ask why? – alvaro Mar 10 '13 at 11:46
  • The reason is that the only place where the program call "uncommon" methods of the children, is in a unique function like your "DoStuff", which can be made visible to other coders, and I prefer to have control on the base class. Sorry for being obscure - I'll try to put an example later of what I am doing, but basically these objects are sort of widgets with common methods for "drawing" (actually is an embedded electronic project, and drawing is done with a laser); each "object" has particular methods to acquire and query sensed data, and also they have different behaviours (motion, etc). – alvaro Mar 10 '13 at 12:02
  • Sort of "UI buttons" (and that's the ONLY reason I thought about the design methodology in the model-view-controller in iOS and the UIButton class... ). But yes, thanks to your explanation I see things much clearer: something has to give. Either the base class, or the DoStuff class NEEDS to be maintained. – alvaro Mar 10 '13 at 12:02
  • I don't think I have made myself clear enough. Do not to variant A (maintain both base class and DoStuff), and do not do variant B (maintain just the DoStuff). Do variant C. If you think it's impossible, show more of your design. – n. m. could be an AI Mar 10 '13 at 13:26
0

If you are planning to create many different children classes in different files, perhaps made by different people, and also I would guess you don't want to change your main code for every child class. Then I think what you need to do in your base class is to define several (not to many) virtual functions (with empty implementation) BUT those functions should be used to mark a time in the logic where they are called like "AfterInseart" or "BeforeSorting", Etc.
Usually there are not to many places in the logic you wish a derived classes to perform there own logic.

Roee Gavirel
  • 18,955
  • 12
  • 67
  • 94