3

I got the classic Shape hierarchy example...

struct Shape { // abstract type
    Shape (int x, int y);

    int x;
    int y;
};

struct Rectangle : public Shape {
    Rectangle (int x, int y, int w, int h);

    int w;
    int h;
};

struct Circle : public Shape {
    Circle (int x, int y, int r);

    int r;
};

a Shapes container, filled with Rectangles and Circles

std::list<Shape*> container;

and printing functions (in my case, those are collision detection functions)

void print_types (Shape&, Shape&) {
    std::cout << "Shape, Shape" << std::endl;
}

void print_types (Rectangle&, Rectangle&) {
    std::cout << "Rectangle, Rectangle" << std::endl;
}

void print_types (Rectangle&, Circle&) {
    ...

Course, when I'm doing this:

std::list<Shape*> it;
Rectangle r (0, 0, 32, 32);

for (it = container.begin(); it != container.end(); it++)
     print_types(r, **it);

I don't want to print only "Shape, Shape" lines. I know virtual methods, dynamic_casts and visitors. But is there any elegant way to get out of it without those solutions and keep my external functions ?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Simon
  • 31
  • 2

4 Answers4

7

You should probably stick with virtual functions and have only one print_types function

void print_types(Shape&)

Then add a virtual PrintName function to the base class and override it in the derived class.

This IS the most elegant way.

Pepe
  • 6,360
  • 5
  • 27
  • 29
4

The short answer is no, function calls are resolved at compile-time. So there is no way (AFAIK) to do this with your existing free functions.

I believe you will have to invest in a double-dispatch mechanism, or do what @Peter suggests in his answer (which sounds more elegant).

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I already tried that solution, it works perfectly but I was looking for a more "simple" way to do this. – Simon May 03 '11 at 21:17
0

I can't call it elegant, and it has several pitfalls, but the classic way to do this before dynamic_cast was to have a virtual function, say virtual char* name(), in Shape and have each derived class override that function to return the correct name.

The most obvious pitfall is that you must maintain the whole thing by hand.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
0

Responding to the edit of the problem:

The best solution is still to find a virtual method that will deliver what is required. For detecting shape collisions, perhaps you could have a function that converts each shape to a polygon (set of lines) and do the detection on that.

C++ uses the type you declared at compile-time to select which overloaded function to call; it doesn't have the ability to make that selection at run-time. That's why you're seeing "Shape,Shape" as output each time. There's a way to help the compiler out but it's going to be tedious. Try to convert each Shape* to the appropriate type, and if it succeeds you can call a more specific function.

I'm not really advocating this; you can see how it gets out of hand with just two shapes, imagine how ugly it gets as you add more! Still it shows how to do what you were trying to do in C++.

void print_types (Rectangle*, Rectangle*) {
    std::cout << "Rectangle, Rectangle" << std::endl;
}

void print_types (Rectangle*, Circle*) {
    ... 

void print_types (Rectangle* left, Shape* right) {
    Rectangle* rightRect = dynamic_cast<Rectangle*>(right);
    if (rightRect != NULL) {
        print_types(left, rightRect);
        return;
    }
    Circle* rightCirc = dynamic_cast<Circle*>(right);
    if (rightCirc != NULL) {
        print_types(left, rightCirc);
        return;
    }
    throw /* something to indicate invalid shape */;
}

void print_types (Circle* left, Shape* right) {
    ...

void print_types (Shape* left, Shape* right) {
    Rectangle* leftRect = dynamic_cast<Rectangle*>(left);
    if (leftRect != NULL) {
        print_types(leftRect, right);
        return;
    }
    Circle* leftCirc = dynamic_cast<Circle*>(left);
    if (leftCirc != NULL) {
        print_types(leftCirc, right);
        return;
    }
    throw /* something to indicate invalid shape */;
}
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622