Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

multiple dispatch

Name: Anonymous 2012-01-21 4:10

Lisp:


[1]> (defclass B () ())
#<STANDARD-CLASS B>
[2]> (defclass C () ())
#<STANDARD-CLASS C>
[3]> (defgeneric f (x y))
#<STANDARD-GENERIC-FUNCTION F>
[4]> (defmethod f ((x B) (y B)) (print "BB"))
#<STANDARD-METHOD (#<STANDARD-CLASS B> #<STANDARD-CLASS B>)>
[5]> (defmethod f ((x B) (y C)) (print "BC"))
#<STANDARD-METHOD (#<STANDARD-CLASS B> #<STANDARD-CLASS C>)>
[6]> (defmethod f ((x C) (y B)) (print "CB"))
#<STANDARD-METHOD (#<STANDARD-CLASS C> #<STANDARD-CLASS B>)>
[7]> (defmethod f ((x C) (y C)) (print "CC"))
#<STANDARD-METHOD (#<STANDARD-CLASS C> #<STANDARD-CLASS C>)>
[8]> (f (make-instance 'B) (make-instance 'B))

"BB"
"BB"
[9]> (f (make-instance 'B) (make-instance 'C))

"BC"
"BC"
[10]> (f (make-instance 'C) (make-instance 'B))

"CB"
"CB"
[11]> (f (make-instance 'C) (make-instance 'C))

"CC"
"CC"
[12]>
Bye.


C++:


#include <iostream>

using namespace std;

struct A;
struct B;
struct C;

struct A {
  virtual ~A() {}

  virtual void f(A* y) = 0;

  virtual void f(B* y) = 0;
  virtual void f(C* y) = 0;

  virtual void g(B* y) = 0;
  virtual void g(C* y) = 0;
};

struct B : public A {
  void f(A* y);

  void f(B* y);
  void f(C* y);

  void g(B* y);
  void g(C* y);
};

struct C : public A {
  void f(A* y);

  void f(B* y);
  void f(C* y);

  void g(B* y);
  void g(C* y);
};

void B::f(A* y) { y->g(this); }

void B::f(B* y) { cout << "BB" << endl; }
void B::f(C* y) { cout << "BC" << endl; }

void B::g(B* y) { y->f(this); }
void B::g(C* y) { y->f(this); }


void C::f(A* y) { y->g(this); }

void C::f(B* y) { cout << "CB" << endl; }
void C::f(C* y) { cout << "CC" << endl; }

void C::g(B* y) { y->f(this); }
void C::g(C* y) { y->f(this); }

int main(int argc, char** argv) {
  A* b = new B();
  A* c = new C();
  b->f(b);
  b->f(c);
  c->f(b);
  c->f(c);
  delete b;
  delete c;
  return 0;


$ g++ gen.cpp
$ a.out
BB
BC
CB
CC

Name: Anonymous 2012-01-22 17:05

>>46,47
Switch statements work if you have a small number of predefined classes with a small number of methods, but it's not extensible. You can't inherit from a class in a library using this object system and extend it without modifying the original dispatcher function. A function pointer mechanism is more scalable and extensible than a huge monolithic switch statement per method. You can replace a single member in the struct and get per-object methods. You can use strings or integers for method names and dynamically resolve function pointers without creating separate functions for each. You can use dynamic linking and add in new classes and methods at runtime.

The dispatch mechanism doesn't have to use an array. It just needs to be able to associate types to function pointers and handle inheritance at runtime. It could be a binary tree, compacted array, or array with a pointer to the base class's table. You could even have several predefined types of dispatch table and then use a switch statement to choose at runtime based on how large and sparse the table is.
void f(generic x, generic y) {
     void (*fn)(generic x, generic y) = ResolveFunction("f",x,y);
     if (!fn) abort(); /* failure, no method for those types */
     return fn(x, y);
}

/* in a separate file */
void RegisterD(void) {
     /* one for each combination of types */
     RegisterFunction(&D_vtable,"f",D_type,B_type,f_DB);
     /* ... */
}

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List