3

Suppose I have something like the following:

class Point : geometry {
   ...
   Point(double x, double y) {
   }
   double distanceTo(Line) {
   }
   double distanceTo(Point) {
   }
}
class Line : geometry {
   ...
   Line(double x, double y, double slopex, double slopey) {
   }
   double distanceTo(Line) {
   }
   double distanceTo(Point) {
   }
}
struct point_t {
    double x, y;
}
struct line_t {
    double x, y, slope_x, slope_y;
}
struct Geom_Object_t {
   int type;
   union {
       point_t p;
       line_t l;
   } geom;
}

I am wondering what the best way to define a dispatch table for a function like

double distanceTo(Geom_Object_t * geom1, Geom_Object_t * geom2) {
}

The classes are written in C++, but the distanceTo function and the struct must be externed to C

thanks

adk
  • 4,479
  • 9
  • 36
  • 38
  • 1
    There *is* no portable way to "extern to C" a `struct` that includes a `union` among classes which are **not** Plain-Old-Data (and Point and Line aren't, as they have method overloads). If you want a non-portable solution for some specific proprietary C++ compiler, mention that; otherwise, rethink your ill-conceived constraints, because what you ask (if the requirement is to be standards compliant and portable) is simply not feasible -- so you may have to use for C purposes e.g. a struct with a _pointer_ (and cast/deref that pointer in the C++ code), that _would_ be OK. – Alex Martelli Jan 16 '10 at 05:19
  • @Alex Marelli sorry about that, fixed. – adk Jan 16 '10 at 05:25

4 Answers4

3

I would make the class diagram different: an abstract base class GeomObject, subclassing geometry (with a getType accessor, as well as pure virtual distanceTo overloads), and concrete subclasses Line and Point of GeomObject (with overrides of the accessor and overloads). The need to "extern C" the double distanceTo function is not a problem, since you're not talking about overloads of that function anyway: you simply want to return geom1.distanceTo(x) (letting the virtual table do that part of the work;-) where x is an appropriate cast, e.g., assuming the class diagram I've explained:

extern "C"
double distanceTo(Geom_Object_t * geom1, Geom_Object_t * geom2) {
  if(geom2->getType() == POINT_TYPE) {
    return geom1->distanceTo(static_cast<Point*>(geom2));
  } else {
    return geom1->distanceTo(static_cast<Line*>(geom2));
  }
}
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • the struct can no longer be used from C, however. – adk Jan 16 '10 at 05:10
  • @adk, no, but you said only the `distanceTo` function needed to be `"extern C"`! – Alex Martelli Jan 16 '10 at 05:11
  • @adk, yep, and with you "clarification" it becomes clear that your original solution also breaks your constraints -- to "extern C" a struct, it has to be a "plain-old-data" one, so in particular no union with classes of which one or more have overloaded methods... externing **that** to C is not standard/portable. So rethink your constraints or add a level of indirectness via a pointer (which would make my answer be fully `"extern C"`able just as well of course;-). – Alex Martelli Jan 16 '10 at 05:21
  • @Alex Martelli sorry about that fixed. – adk Jan 16 '10 at 05:26
  • @adk, so now you just need to **build** a `Point` or `Line` as needed (depending on `type`) for each operand, my answer still applies when you do that (just call the abstract base class `Foobar` since you completely redefined the meaning of the arguments' pointed-to class, `Geom_object_t` -- do you need me to spell that out, really?-) – Alex Martelli Jan 16 '10 at 05:32
2

I would use double dispatch with Visitor pattern. Then you only have to have two pointers to geometry objects and let double dispatch call the appropriate virtual distanceTo function based on actual dynamic types of two objects, which you can do from your C function.

Alex B
  • 82,554
  • 44
  • 203
  • 280
1

(updated to match updated question)

To avoid duplication move your conversion code in one helper function and let C++ do the rest of the work:

geometry makeg(Geom_Object_t* g) {
    switch(g->type) {
         case TYPE_POINT: return Point(g->geom.p.x, g->geom.p.y);
         case TYPE_LINE : return Line(g->geom.l.x, g->geom.l.y, g->geom.l.slope_x, g->geom.l.slope_y);
         // ...
    }
}

makeg(geom1).distanceTo(makeg(geom2));
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
0

could you do something simple like:

if (g1->type == LINE) {
  if (g2->type == LINE) return g1->distance(g2->l);
  if (g2->type == POINT) ...
}
else ...

you can implement this part in C++ and expose the function through the extern "C"

then you could perhaps provide a method in your geometrical classes to accept geometrical struct as a parameter and perform dispatching inside the classes using regular C++ function overload.

Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • Not if you have 10 different types, and 20 methods for each – adk Jan 16 '10 at 05:11
  • @adk, no, but by letting C++ pick the right override, as in my answer, it's an `O(M)` problem in terms of code size (best if `M <= N`, of course, but how do you ever end up with `M != N` when the methods' overloads should obviously be one per type?!). – Alex Martelli Jan 16 '10 at 05:15