views:

146

answers:

5

Suppose I have a bunch of fruit:

class Fruit { ... };
class Apple : public Fruit { ... };
class Orange: public Fruit { ... };

And some polymorphic functions that operate on said fruit:

void Eat(Fruit* f, Pesticide* p)   { ... }
void Eat(Apple* f, Pesticide* p)   { ingest(f,p); }
void Eat(Orange* f, Pesticide* p)   { peel(f,p); ingest(f,p); }

OK, wait. Stop right there. Note at this point that any sane person would make Eat() a virtual member function of the Fruit classes. But that's not an option, because I am not a sane person. Also, I don't want that Pesticide* in the header file for my fruit class.

Sadly, what I want to be able to do next is exactly what member functions and dynamic binding allow:

typedef list<Fruit*> Fruits;
Fruits fs;
...
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
    Eat(*i);

And obviously, the problem here is that the pointer we pass to Eat() will be a Fruit*, not an Apple* or an Orange*, therefore nothing will get eaten and we will all be very hungry.

So what I really want to be able to do instead of this:

Eat(*i);

is this:

Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i));

But to my limited knowledge, such magic does not exist, except possibly in the form of a big nasty if-statement full of calls to dynamic_cast.

So is there some run-time magic of which I am not aware? Or should I implement and maintain a big nasty if-statement full of dynamic_casts? Or should I suck it up, quit thinking about how I would implement this in Ruby, and allow a little Pesticide to make its way into my fruit header?

Update: Instead of the contrived bit with the bare Eat functions and Pesticide, suppose instead that I just don't want to put Eat in the fruit because it makes no sense. A fruit that knows how to eat itself? Pshaw. Instead I need an Eater class with an Eat function, with different code for eating each kind of fruit, and some default code in case it's a fruit that the eater doesn't recognize:

class Eater
{
public:
  void Eat(Apple* f) { wash(); nom(); }
  void Eat(Orange* f) { peel(); nom(); }
  void Eat(Fruit* f) { nibble(); }
};
...
Eater me;
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i)
  me.Eat(*i);  //me tarzan! me eat!

But again, this doesn't work, and the straightforward solution in C++ seems to be a bunch of calls to dynamic_cast.

However, as one of the answers suggests, there may be another clever solution. What if Fruits exposed the qualities that mattered to eaters, with functions like MustPeel() and MustWash()? Then you could get by with a single Eat() function ...

Update: Daniel Newby points out that using Visitor also solves the problem as presented ... but this requires a bit of a semantic headstand (Fruit::use or Fruit::beEaten?).

While I'd like to accept several answers, I think psmears's answer is actually the best one for future readers. Thanks, everyone.

+6  A: 

You need to redesign. Namely, do everything you seem to be avoiding (for what reason, who knows.)

Polymorphic behavior requires polymorphic functions. This means a virtual function. (Or your ladder of dynamic_cast's, which completely defeats the purpose...)

// fruit.h
class Pesticide; // you don't need a complete type

struct Fruit
{
    virtual void Eat(Pesticide*) = 0;
};

// apple.h
class Apple : public Fruit
{
    void Eat(Pesticide* p) { ... }
};

// orange.h
class Orange : public Fruit
{
    void Eat(Pesticide* p) { ... }
};

If you still want a free function*:

void Eat(Fruit* f, Pesticide* p)   { f->Eat(p); }

*Note that your post is already indicative of bad design; namely the first Eat function:

void Eat(Fruit* f, Pesticide* p)   { }

When does doing nothing to a fruit equate to eating the fruit? A pure virtual function is a much better interface choice.

GMan
Enh, I think it's just indicative of bad example ... here, I'll put in some magical ellipses.
Matthew Lowe
A: 

There's nothing wrong with having arbitrary class pointers in headers. They form the basis of many idioms, like PIMPL and opaque pointers. Also, if you aren't a sane person, how are you supposed to understand my answer?

Seriously, derived functions and polymorphism exist to solve this problem. If you refuse to use the language provided tools, why bother using it at all? Any solution you can come up with can be translated into a virtual function call in any case, just you would have coded it manually instead of having the compiler do it.

DeadMG
+3  A: 

When a question like this comes up, it's good to look at exactly why you want to make particular decisions - for instance, why do you not want the Fruit classes to know about Pesticide?

I'm sure there is a good reason for this - but expressing that reason will help clarify in your mind exactly what your aims are - and this often sheds a new light on a possible angle for structuring the program.

For instance, you might end up adding new virtual methods "IsEdible" and "PrepareForEating". Then you can implement these for each fruit, and implement one generic Eat method that works for all fruits - and ingests the pesky pesticide too - all without the Fruit classes knowing anything about it.

Of course, depending on your precise aims, that may be totally inappropriate - which is why you'll have to clarify the example in your own head :-)

psmears
Aha! So in addition to "dynamic_cast everything" and "have the fruit Eat itself", there is a third way: have the fruit expose enough information about itself that the Eater is able to know what to do with it, and have only one Eat() function. I like this answer.
Matthew Lowe
A: 

What you're asking for isn't possible. The function overloading resolution needs to know at compile time which class the parameter is so it can call the correct Eat function. The only exception is for virtual member functions, which you've already ruled out.

Mark Ransom
+2  A: 

Just use the I Am Standing Right Here! Pattern. It's like the Visitor Pattern but without a container.

// fruit.h
class Fruit;
class Apple;
class Orange;

class Fruit_user {
    public:
        Fruit_user();
        virtual ~Fruit_user();
        virtual use(Apple *f) = 0;
        virtual use(Orange *f) = 0;
};

class Fruit {
    public:
        // Somebody with strong template fu could probably do
        // it all here.
        virtual void use(Fruit_user *fu) = 0;
};

class Apple : public Fruit {
    public:
        virtual void use(Fruit_user *fu) {
            fu->use(this);
        }
};

class Orange: public Fruit {
    public:
        virtual void use(Fruit_user *fu) {
            fu->use(this); 
        }
};


// dow-chemical.h
class Pesticide_fruit_user : public Fruit_user {
    public:
        Pesticide_fruit_user(Pesticide *p) {
            p_ = p;
        }

        virtual void use(Apple *f) { ingest(f, p_); }
        virtual void use(Orange *f) { peel(f, p_); ingest(f, p_); }

    private:
        Pesticide *p_;
};
Daniel Newby
Another good answer that provides another new solution for the problem presented in the original post. Using "I am standing right here" (like visitor) provides double-dispatch and gets the pesticide out of the header, all as requested.
Matthew Lowe
I must be getting dumber... but I see a very common `Visitor` here. http://en.wikipedia.org/wiki/Visitor_pattern What's the so fundamental difference ?
Matthieu M.
Hm. I was thinking that Daniel meant that this is different because you only ever have one kind of visitor ... but even if that's right, I suppose it's just a particular way of using Visitor.
Matthew Lowe
The proper visitor traverses some kind of container, taking the visitor object to visit each containee, hence the name. Something has to take the worker to the data because it is traversal agnostic. In I Am Standing Right Here, the "visitor" already knows where the object is, but does not know _what_ it is. (Note that the visitor pattern could be applied on top of my pattern. For example, to visit each fruit in a bunch of grapes.)
Daniel Newby
@Troubadour: Thanks for the patch.
Daniel Newby