Multiple Dispatch -- Better Than Item 31?

113 views
Skip to first unread message

merl...@my-deja.com

unread,
Nov 16, 1999, 3:00:00 AM11/16/99
to
[ note to moderators: first time poster to clc++m. Sorry about the
verbosity, but this needs a lot of sample code... ]

I've been thinking about Scott Meyers' discussion of multiple dispatch
in _More Effective C++_ item 31. (Using the example of a video game
with spaceships, asteroids and space stations moving around, Scott
explores the possibilities of different ways to process collisions
between different objects.) What Scott eventually comes up with is a
map of pointers to collision-processing functions, keyed by <string,
string> pairs. Unfortunately, as Scott says on page 248, "everything
we've done will work fine as long as we never need to allow
inheritance-based type conversions when calling collision-processing
functions". The problem is that Scott's design uses the type names of
the GameObjects, thusly:


void processCollision( GameObject& object1,
GameObject& object2 )
{
HitFunctionPtr phf = lookup( typeid(object1).name(),
typeid(object2).name() );
if (phf) phf( object1, object2 )
else throw UnknownCollision( object1, object2 );
}

HitFunctionPtr lookup( const string& class1,
const string& class2 )
{
static auto_ptr<HitMap>
collisionMap( initializeCollisionMap() );

HitMap::iterator mapEntry =
collisionMap->find( make_pair(class1, class2 ));

if ( mapEntry == collisionMap->end() ) return 0;

return (*mapEntry).second;
}

HitMap * initializeCollisionMap()
{
HitMap *phm = new HitMap;

(*phm)[ make_pair("Spaceship", "Asteroid") ]
= &shipAsteriod;
...
}

In other words, this design works fine for Spaceship, SpaceStation and
Asteroid objects. On the other hand, suppose you introduce
MilitarySpaceship and CivilianSpaceship classes inheriting from
Spaceship, which should have the same collision behavior. Unless you
explicitly add entries to the collisionMap for the Spaceship subclasses
(can you say "maintenance nightmare?"), the lookup() function will throw
an UnknownCollision exception whenever presented with a
MilitarySpaceship.

(If you're still confused, go reread item 31 -- Scott tells this so much
better than I can. If you don't have a copy of _More Effective C++_, go
out and buy one; you can use the tips and I'm sure Scott can use the
royalties.)

The weakness here is the use of attributes of the typeid object to
generate the collisionMap keys. The problem is that we need to get the
same identifier both *with* a GameObject (in the lookup() function) and
*without* a GameObject (in the initializeCollisionMap() function). The
obvious trick would be to add a static virtual function getKey() to the
classes derived from GameObject -- but you can't have a static virtual
function. Then I thought of having two functions, one static and one
virtual, both using the same internal data, like so:


class GameObject
{
public:
virtual const string& getKey() const = 0;
...
}

class Spaceship: public GameObject
{
public:
virtual const string& getKey() const
{ return myName; };
static const string& getKeyStatic()
{ return myName; };
private:
static const string myName("Spaceship");
...
}

class Asteroid: public GameObject
{
public:
virtual const string& getKey() const
{ return myName; };
static const string& getKeyStatic()
{ return myName; };
private:
static const string myName("Asteroid");
...
}

class MilitarySpaceship: public Spaceship
{
public:
// I want MilitarySpaceships to act like Spaceships,
// so I don't provide a getKey();
// instead, I inherit Spaceship::getKey()
...
}

class CivilianSpaceship: public Spaceship
{
public:
// ditto for CivilianSpaceships
...
}

HitMap * initializeCollisionMap()
{
HitMap *phm = new HitMap;

(*phm)[ make_pair(Spaceship::getKeyStatic(),
Asteroid::getKeyStatic() ) ]
= &shipAsteroid;
...
}

void processCollision( GameObject& object1,
GameObject& object2 )
{
HitFunctionPtr phf = lookup( object1.getKey(),
object2.getKey() );
if (phf) phf( object1, object2 )
else throw UnknownCollision( object1, object2 );
}

Now I'm using a virtual function for the calls to lookup() and a static
function for the calls to initializeCollisionMap(), and as a bonus I've
also gotten rid of those irritating non-portable magic strings in
initializeCollisionMap(). (I hates magic strings, I does :-) Not only
that, but now my MilitarySpaceships and CivilianSpaceships both use
Spaceship collision logic. Problem solved, right?

Well, at least until I decide to add a Torpedo class (deriving from
MilitarySpaceship) which needs its own collision-processing logic.
(Torpedoes, of course, go "boom!" when they hit things.) I have to
write something like this:

class Torpedo: public MilitarySpaceship
{
public:
virtual const string& getKey() const
{ return myName; };
static const string& getKeyStatic() // see below
{ return myName; };
private:
static const string myName("Torpedo");
...
}


But I remember that nonvirtual functions are supposed to represent
invariance over specialization, and thus redefinining getKeyStatic() in
subclasses of Spaceship is a no-no. Rats.

But I'm stubborn, and I come up with another idea -- move the statics
out of the GameObject hierarchy and into their own class, thusly:

class GameObjectKeys
{
// This could go into the CollisionMap class
// that Scott mentions on page 249
public:
static const string& Spaceship("Spaceship");
static const string& Asteroid("Asteroid");
static const string& SpaceStation("SpaceStation");
// Note that we're not going to define anything
// for MilitarySpaceship or CivilianSpaceship
// since we want them to behave like Spaceships
// But I do want to define something for Torpedo
static const string& Torpedo("Torpedo");
}

class GameObject
{
// unchanged
public:
virtual const string& getKey() const = 0;
...
}


class Spaceship: public GameObject
{
public:
virtual const string& getKey() const
{ return GameObjectKeys::Spaceship; };
...
}

class MilitarySpaceship: public Spaceship
{
public:
// unchanged
...
}

class CivilianSpaceship: public Spaceship
{
public:
// likewise unchanged
...
}

class Torpedo: public MilitarySpaceship
{
public:
virtual const string& getKey() const
{ return GameObjectKeys::Torpedo; };
...
}

class Asteroid: public GameObject
{
public:
virtual const string& getKey() const
{ return GameObjectKeys::Asteroid; };
...
}

HitMap * initializeCollisionMap()
{
HitMap *phm = new HitMap;

(*phm)[ make_pair(GameObjectKeys::Spaceship,
GameObjectKeys::Asteroid ) ]
= &shipAsteriod;
(*phm)[ make_pair(GameObjectKeys::Torpedo,
GameObjectKeys::Asteroid ) ]
= &torpedoAsteroid;
...
}

*Now* I think I've got it. My MilitarySpaceships collide just like
CivilianSpaceships, while my Torpedos have their own handling logic.
initializeCollisionMap() gets its key values from the same place the
GameObjects do. I don't have any extra magic strings laying around.
I'm not doing nasty things with redefining non-virtual functions. Of
course, I'm not all that happy about the coupling between GameObjectKeys
and the GameObjects hierarchy, but sometimes you've gotta do what you've
gotta do.

So my questions to the C++ guruship at large are:
1) Will this work? Or am I missing something that would be blindingly
obvious were I only more knowledgeable?
2) Why _can't_ you have a virtual static function? There must be a good
reason, but I have no clue as to what that good reason is.

--
Edmund Schweppe aka merl...@my-deja.com
Blissfully free of official positions


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


acu...@my-deja.com

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
In article <80rvhs$4tk$1...@nnrp1.deja.com>,

merl...@my-deja.com wrote:
> But I remember that nonvirtual functions are supposed to represent
> invariance over specialization, and thus redefinining getKeyStatic()
in
> subclasses of Spaceship is a no-no. Rats.

Where does the std forbid redefining static functions in derived
classes? I've been doing it for years. If there is a requirement like
that, it's unreasonable and a defect report shall be filed. If
this "nonvirtual functions are supposed to represent invariance over
specialization" is yet another "rule of thumb". You just found an
exception.

> So my questions to the C++ guruship at large are:
> 1) Will this work? Or am I missing something that would be blindingly
> obvious were I only more knowledgeable?

Yes it works. I've been doing this for years. MEC++ item 31 is really
not state of the art. I've personally thought up at least 2 C++ idioms
to implement multiple dispatch better (more extendible, more flexible
and more efficient) than item 31. I do thank Scott for the wonderful
book, which I got the first edition (in 1996) with Scott's signature.
It really gave me a good start to think about the more interesting
aspects of C++.

> 2) Why _can't_ you have a virtual static function? There must be a
good
> reason, but I have no clue as to what that good reason is.

because "virtual static function" is an oxymoron. virtual is
wrt "this" - the object, you don't need an object to call a static
function. BTW, I do understand what YOUR "virtual static function"
mean, just write:

virtual const string& getKey() const {
static string key("Whatever");
return key;
}

and get rid of the dummy class.

A.

merl...@my-deja.com

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
In article <80rvhs$4tk$1...@nnrp1.deja.com>, merl...@my-deja.com wrote:

[ snippage el grande ]

I've gotten a couple of email responses to my question

> 2) Why _can't_ you have a virtual static function? There must be a
> good reason, but I have no clue as to what that good reason is.

which tell me that perhaps I didn't really ask the question I wanted to.
(Thanks to Drew Davis, Douglas Kirkpatrick and Perry Rapp for your
answers to the original question.) Let me try again.

I know that when I invoke a virtual function on an *object* through a
pointer-to-base or reference-to-base, the function call is dynamically
dispatched through the vtbl. Thus, the compiler knows enough at compile
time to set up the appropriate vtbl, and the runtime environment knows
where to look for the vtbl when necessary. Thus, given the code below:

// incomplete classes
class B
{
public:
virtual void foo()
{ cout << "B.foo()" << endl; }
}
class D1: public B
{
public:
virtual void foo()
{ cout << "D1.foo()" << endl; }
}
class D2: public B
{
public:
// no redefinition of foo()
}
class D2A: public D2
{
public:
virtual void foo()
{ cout << "D2A.foo()" << endl; }
}

creating an object bar of type D2 and invoking bar.foo() actually ends
up calling B::foo() with *this pointing to bar. (Specifically,
bar.foo() translates roughly to (bar.vtbl[foo_offset])(&bar) where
foo_offset is whatever the appropriate offset is for foo in bar's vtbl.)
Obviously, for this mechanism to work, the compiler must be able to
generate code to populate the vtbl, recognizing that D2A::foo() should
not be dispatched to B::foo() while D2::foo() should.

What I meant by "virtual static function" was a virtual function (e.g.,
one that can legally be redefined through the C++ inheritance
mechanisms) that is also static (e.g., one that does *not* need access
to *this and thus can be called as a class method). Thus, I'd like to be
able to write something like:

class B
{
public:
static virtual void bletch()
{ cout << "B::bletch()" << endl; }
}
class D1: public B
{
public:
static virtual void bletch()
{ cout << "D1::bletch()" << endl; }
}
class D2: public B
{
public:
// no redefinition of bletch() either
}
class D2A: public D2
{
public:
static virtual void bletch()
{ cout << "D2A::bletch()" << endl; }
}

and then be able to write mainline code thusly:

void main()
{
B::bletch(); // should print "B::bletch()"
D1::bletch(); // should print "D1::bletch()"
D2::bletch(); // should print "B::bletch()"
D2A::bletch(); // should print "D2A::bletch()"
}

It seems to me that there's enough information available to the compiler
to be able to tell that D2 doesn't override B::bletch() while D1 and D2A
do, even without a *this pointer.

My question then is -- I know I can't do this, but why?

Andrei Alexandrescu

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
<acu...@my-deja.com> wrote in message news:80somp$pel$1...@nnrp1.deja.com...

> I've personally thought up at least 2 C++ idioms
> to implement multiple dispatch better (more extendible, more flexible
> and more efficient) than item 31.

I'd be interested to hear about these. I guess they're on topic, so you may
want to post them.

> > 2) Why _can't_ you have a virtual static function? There must be a
> good
> > reason, but I have no clue as to what that good reason is.
>

> because "virtual static function" is an oxymoron.

The simple reason that something is an oxymoron is not reason enough for
that thing not to exist. For instance: "virtual constructor", "dynamic
overloading", or... "millitary intelligence" :o).

"Dynamic overloading" happens to be tightly linked with the discussion we
are having here. That's a short way for me to say "Making functions virtual
with respect to more than one object". (Does anyone agree?)

Basically the ideal C++ engine that does what Item 31does is:

* you provide a collection of overloaded functions, like:

void Collide(Spaceship&, Spaceship&);
void Collide(Spaceship&, Asteroid&);
void Collide(Asteroid&, Asteroid&);
...

* you call the engine passing it the name of the function and two references
to GameObject:

GameObject& obj1 = ...;
GameObject& obj2 = ...;
Item31Engine(Collide, obj1, obj2);

The magic function Item31Engine will figure out the dynamic types of obj1
and obj2, and will call the appropriate overload, as if you did the casts
yourself.

The code above is not valid C++. I said "the ideal C++ engine". However, one
can get surprisingly close to it.


Andrei

Stefan Seefeld

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
Andrei Alexandrescu wrote:
>
> <acu...@my-deja.com> wrote in message news:80somp$pel$1...@nnrp1.deja.com...

> > > 2) Why _can't_ you have a virtual static function? There must be a


> > good
> > > reason, but I have no clue as to what that good reason is.

because the exact type of an object is known only by the object itself.
So to do the correct dispatch you have to inspect the object, which isn't
possible in a static method since there is no object to inspect.

> "Dynamic overloading" happens to be tightly linked with the discussion we
> are having here. That's a short way for me to say "Making functions virtual
> with respect to more than one object". (Does anyone agree?)
>
> Basically the ideal C++ engine that does what Item 31does is:
>
> * you provide a collection of overloaded functions, like:
>
> void Collide(Spaceship&, Spaceship&);
> void Collide(Spaceship&, Asteroid&);
> void Collide(Asteroid&, Asteroid&);
> ...
>
> * you call the engine passing it the name of the function and two references
> to GameObject:
>
> GameObject& obj1 = ...;
> GameObject& obj2 = ...;
> Item31Engine(Collide, obj1, obj2);
>
> The magic function Item31Engine will figure out the dynamic types of obj1
> and obj2, and will call the appropriate overload, as if you did the casts
> yourself.
>
> The code above is not valid C++. I said "the ideal C++ engine". However, one
> can get surprisingly close to it.

I wonder why the keyword 'Visitor' hasn't been mentioned yet. Whenever certain
conditions are matched (at least one of both class hierarchies is stable and relatively
small) it is the ideal way in C++ to do binary dispatch based on runtype polymorphism.

Stefan
_______________________________________________________

Stefan Seefeld
Departement de Physique
Universite de Montreal
email: seef...@magellan.umontreal.ca

_______________________________________________________

...ich hab' noch einen Koffer in Berlin...

Michiel Salters

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
merl...@my-deja.com wrote:

> In article <80rvhs$4tk$1...@nnrp1.deja.com>, merl...@my-deja.com wrote:

> [ snippage el grande ]

Good examples are to be followed...

> I've gotten a couple of email responses to my question

> > 2) Why _can't_ you have a virtual static function? There must be a


> > good reason, but I have no clue as to what that good reason is.

> which tell me that perhaps I didn't really ask the question I wanted to.

Indeed it must - when determining the layout of D2::__vtbl

> What I meant by "virtual static function" was a virtual function (e.g.,
> one that can legally be redefined through the C++ inheritance
> mechanisms) that is also static (e.g., one that does *not* need access
> to *this and thus can be called as a class method). Thus, I'd like to be
> able to write something like:

Aha - you think you can only override virtual functions. This isn't
true, e.g.
class Bnv {
void foo() { cout << "B's non-virtual foo\n"; }
}
class Dnv {
void foo() { cout << "D's non-virtual foo\n"; }
}

is perfecly legal, and with

Bnv b; Dnv d;
b.foo();
d.foo();

The result is:

B's non-virtual foo
D's non-virtual foo

The surprise is in
Bnv *base;
base=&b; base->foo();
base=&d; base->foo()

which prints.

B's non-virtual foo
B's non-virtual foo

Because *base is a Bnv.

> class B
> {
> public:
> static virtual void bletch()
> { cout << "B::bletch()" << endl; }
> }
> class D1: public B
> {
> public:
> static virtual void bletch()
> { cout << "D1::bletch()" << endl; }
> }
> class D2: public B
> {
> public:
> // no redefinition of bletch() either
> }
> class D2A: public D2
> {
> public:
> static virtual void bletch()
> { cout << "D2A::bletch()" << endl; }
> }

> and then be able to write mainline code thusly:

> void main()
> {
> B::bletch(); // should print "B::bletch()"
> D1::bletch(); // should print "D1::bletch()"
> D2::bletch(); // should print "B::bletch()"
> D2A::bletch(); // should print "D2A::bletch()"
> }

> It seems to me that there's enough information available to the compiler
> to be able to tell that D2 doesn't override B::bletch() while D1 and D2A
> do, even without a *this pointer.

Yes -and if you leave off the virtual, the above program is just fine, and
will work as expected. Virtual is only needed if you call a function on
an object of a derived class via a pointer to the base class part.
You can't use pointers to a base class to call static member functions.

HTH,

Michiel Salters

Hyman Rosen

unread,
Nov 17, 1999, 3:00:00 AM11/17/99
to
merl...@my-deja.com writes:
> It seems to me that there's enough information available to the compiler
> to be able to tell that D2 doesn't override B::bletch() while D1 and D2A
> do, even without a *this pointer.
>
> My question then is -- I know I can't do this, but why?

Oh, my. You are confused :-) This will work exactly the way you
want if you declare your functions static. There's no virtualness
needed.

acu...@my-deja.com

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
In article <38331411...@magellan.umontreal.ca>,

Stefan Seefeld <seef...@magellan.umontreal.ca> wrote:
> I wonder why the keyword 'Visitor' hasn't been mentioned yet.
Whenever certain
> conditions are matched (at least one of both class hierarchies is
stable and relatively
> small) it is the ideal way in C++ to do binary dispatch based on
runtype polymorphism.

Hmmm... looks like you haven't read the "More Effective C++" yet.
the "vistor" pattern is actually the first scheme mentioned in item 31.
It's simple and efficient. But it suffers from the dependence of vistor
interface upon concrete elements. i.e., everytime you add a new
element, the vistor interface needs to be modified. This is
unacceptible if you are providing an extendible framework/library for
vistors and elements -- interacting polymorphic objects. This can be
easily solved by implement a vtbl like dispatcher in the vistor
interface (the book provided a simple minded implementation which is
neither very extendible nor efficient.)

The map of non-member functions solution is very extendible and mostly
ideal for symmetric multiple dispatching, but it suffers from
inefficiency (every call involves costly key construction and map
lookup) which may or may not be an issue depending up the type of
application. Also using non-member functions to deal with interaction
between objects usually implies friends galore or over publicity.

A.

Ray Tayek

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
In article <38331411...@magellan.umontreal.ca>,
seef...@magellan.umontreal.ca says...

> Andrei Alexandrescu wrote:
> >
> > <acu...@my-deja.com> wrote in message news:80somp$pel$1...@nnrp1.deja.com...
>
> > ...
> > void Collide(Spaceship&, Spaceship&);
> > void Collide(Spaceship&, Asteroid&);
> > void Collide(Asteroid&, Asteroid&);
> > ...
>
> I wonder why the keyword 'Visitor' hasn't been mentioned yet. ...

me too, robert martin posted a sample of triple dispatch a while ago that
was similar to this kind of thing. I hacked it up for a java presentation
using monsters (which could have a deep inhertance tree), but its similar
to c++:

// visitor with triple dispatch. from a post to comp.object by robert
martin http://www.oma.com
/*
In this case, we are actually using a triple dispatch, because we have
two
types to resolve. The first dispatch is the virtual Attacks function
which
resolves the type of the object upon which Attacks is called. The second
dispatch is the virtual Accept function which resolves the type of the
object passed into Attacks. Now that we know the type of both objects,
we
can call the appropriate global function to calculate the Attack. This
is done by the third and final dispatch to the Visit function.
*/
interface Monster
{
Monster attacks(final Monster monster);
void accept(MonsterVisitor visitor);
}
class Orc implements Monster
{
public Monster attacks(final Monster monster)
{
OrcVisitor visitor=new OrcVisitor(this);
monster.accept(visitor);
return visitor.monster();
}
public void accept(MonsterVisitor visitor)
{ visitor.visit(this); } // visit Orc
}
class Dragon implements Monster
{
public Monster attacks(final Monster monster)
{
DragonVisitor visitor=new DragonVisitor(this);
monster.accept(visitor);
return visitor.monster();
}
public void accept(MonsterVisitor visitor)
{ visitor.visit(this); } // visit Dragon
}
// visitors.
interface MonsterVisitor
{
abstract public void visit(Orc orc);
abstract public void visit(Dragon dragon);
}
class DragonVisitor implements MonsterVisitor
{
DragonVisitor(final Dragon dragon)
{ this.dragon=dragon; }
public void visit(Orc orc)
{ monster=Interaction.attacks(dragon,orc); }
public void visit(Dragon dragon)
{ monster=Interaction.attacks(this.dragon,dragon); }
Monster monster() {return monster; }
private Monster monster;
private final Dragon dragon;
}
class OrcVisitor implements MonsterVisitor
{
OrcVisitor(final Orc orc)
{ this.orc=orc; }
public void visit(Orc orc)
{ monster=Interaction.attacks(this.orc,orc); }
public void visit(Dragon dragon)
{ monster=Interaction.attacks(orc,dragon); }
Monster monster() {return monster; }
private Monster monster;
private final Orc orc;
}
// interactions
class Interaction
{
static Monster attacks(final Dragon d,final Dragon d2)
{ System.out.print(d+" vs "+d2); return d; }
static Monster attacks(final Dragon d,final Orc o)
{ System.out.print(d+" vs "+o); return d; }
static Monster attacks(final Orc o,final Dragon d)
{ System.out.print(o+" vs "+d); return d; }
static Monster attacks(final Orc o,final Orc o2)
{ System.out.print(o+" vs "+o2); return o; }
}
public class MartinsVisitor
{
public static void main (String[] args)
{
Monster monster1=new Orc(); System.out.println(monster1);
Monster monster2=new Orc(); System.out.println(monster2);
Monster monster3=new Dragon();
System.out.println(monster3);
System.out.println(", "+monster1.attacks(monster2)+"
survived");
System.out.println(", "+monster1.attacks(monster3)+"
survived");
System.out.println(", "+monster3.attacks(monster1)+"
survived");
for(;;);
}
}

hth
--
Ray (will hack java for food) http://home.pacbell.net/rtayek/
hate Spam? http://www.blighty.com/products/spade/

Edward Jason Riedy

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
And acu...@my-deja.com writes:
-
- Hmmm... looks like you haven't read the "More Effective C++" yet.
- [...] This is
- unacceptible if you are providing an extendible framework/library for
- vistors and elements -- interacting polymorphic objects.

And you haven't read John Vlissides's Pattern Hatching column in
the C++ Report. ;) In the book form, look at the sections on
Visitor Caveats and Visitor Revisited. He also has yet another
Visitor-dispatch solution in this month's C++ Report that's worth
reading. It feels like a compromise between a Visitor and a map/tree.

And for general double-dispatch implementation hints, look at your
local CLOS implementation's code. IIRC, some free implementations
for Scheme use a caching, optimized map (STklos and guile's GOOPS).
They're worth examining if you need a fully general solution.

Jason

acu...@my-deja.com

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
In article <80t00f$ueg$1...@nnrp1.deja.com>,

merl...@my-deja.com wrote:
> In article <80rvhs$4tk$1...@nnrp1.deja.com>, merl...@my-deja.com wrote:
> What I meant by "virtual static function" was a virtual function
(e.g.,
> one that can legally be redefined through the C++ inheritance
> mechanisms) that is also static (e.g., one that does *not* need access
> to *this and thus can be called as a class method). Thus, I'd like to
be
> able to write something like:
>
> class B
> {
> public:
> static virtual void bletch()
> { cout << "B::bletch()" << endl; }
> }
> class D1: public B
> {
> public:
> static virtual void bletch()
> { cout << "D1::bletch()" << endl; }
> }
> class D2: public B
> {
> public:
> // no redefinition of bletch() either
> }
> class D2A: public D2
> {
> public:
> static virtual void bletch()
> { cout << "D2A::bletch()" << endl; }
> }
>
> and then be able to write mainline code thusly:
>
> void main()
> {
> B::bletch(); // should print "B::bletch()"
> D1::bletch(); // should print "D1::bletch()"
> D2::bletch(); // should print "B::bletch()"
> D2A::bletch(); // should print "D2A::bletch()"
> }
>
> It seems to me that there's enough information available to the
compiler
> to be able to tell that D2 doesn't override B::bletch() while D1 and
D2A
> do, even without a *this pointer.
>
> My question then is -- I know I can't do this, but why?

get rid of the virtual keyword, add necessary semicolons at the end of
class definitions, change void main to int main, you've got a legal
code that behaves exactly the way you wanted. What's the problem?

Looks like you're confusing "overriding" and "hiding". ie., when
dealing with non-virtual functions, we don't call it "overriding". std
mandates that variables and/or non-virtual functions "hides" the vars
and/or non-virtual functions of the same name (name only) in base
classes. compiler may (or may not) issue a warning if overloaded
(virtual or not) functions are partially redefined in derived classes.

A.

Dave Harris

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
acu...@my-deja.com () wrote:
> > 2) Why _can't_ you have a virtual static function?
>
> because "virtual static function" is an oxymoron.

You can have them in Smalltalk.

The Smalltalk approach is to reify classes at runtime. That is, for each
compile-time class there is a runtime object which represents it. The
static variables become instance variables of that object. Static
functions become ordinary functions, and the class object forms the "this"
pointer and stores the vtbl (or whatever) for virtual functions.

For this to be useful, different classes must have different instance
variables and so must be different types. Call the class of a class the
"metaclass"; we're saying different classes must have different
metaclasses. And then it all works. A static method of the base class can
be overridden by a static method of the derived class.

It is beautiful, simple and powerful. It's also fairly hard to get your
head around, to teach etc. It involves extra runtime overhead - eg we've
added at least 2 runtime objects to the system for every class. There may
be issues of compatibility with C, I don't know.

Stroustrup briefly mentions the idea in D&E, under "Meta-Objects"
$14.2.8.1. He adds type-checking issues to the list of problems.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Andrei Alexandrescu

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
Stefan Seefeld <seef...@magellan.umontreal.ca> wrote in message
news:38331411...@magellan.umontreal.ca...

> I wonder why the keyword 'Visitor' hasn't been mentioned yet.
Whenever certain
> conditions are matched (at least one of both class hierarchies is
stable and relatively
> small) it is the ideal way in C++ to do binary dispatch based on
runtype polymorphism.

Right. With Robert Martin's Acyclic Visitor, things get even better.

Basically you can implement a pretty darn elegant dynamic overloading
engine if the hierarchy(ies) invloved is(are) visitable in the Acyclic
Visitor sense.


Andrei

J.Barfurth

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to

[added comp.std.c++ as this goes more towards the language lawyers]

<merl...@my-deja.com> schrieb in im Newsbeitrag:
80rvhs$4tk$1...@nnrp1.deja.com...

> 2) Why _can't_ you have a virtual static function? There must be a good
> reason, but I have no clue as to what that good reason is.

Contrary to what others have answered, I think the language could allow it.
There may be a rationale for not allowing it, but it escapes me.
The idea is to have per-class behaviour be dispatched on an per-object
basis.

Actually there is one case where we do have a virtual static function in the
language: operator delete
The strange thing about that one is only, that you state whether it should
be 'virtual' at the declaration of the destructor.

By analogy one could allow the following:

<EXAMPLE>
struct A
{
virtual static char* name() { return "A"; }
};
struct B
{
virtual static char* name() { return "B"; }
};
int main()
{
A a;
B b;

std::cout << A::name() << B::name() << std::endl;
std::cout << a.name() << b.name() << std::endl;

// now for polymorphic dispatch
A* poly;

poly = &a;
std::cout << poly->name();
poly = &b;
std::cout << poly->name() << std::endl;
}
</EXAMPLE>

Which should print:
AB
AB
AB

I had a need for this behaviour more than once and find the usual workaround
more error-prone:
struct A
{
static char* static_name() { return "A"; }
virtual char* name() { return static_name(); }
};
struct B : A
{
static char* static_name() { return "B"; } // might forget this - it
could be needed in a template
virtual char* name() { return static_name(); } // might forget this,
if not pure in A
};

This is also more difficult to read, as you have to think up another name
for the same behaviour. The requirement that static_name() and name() return
the same value for a most derived object cannot presently be expressed in
the language. The compiler could just generate code like the above (without
the unneeded implicit this argument).

The dispatch of this also resembles the way typeid is resolved:
if called for a type do static (compile-time) dispatch
if called for an object do dynamic (runtime) dispatch

It would even be possible to have a pure static virtual. As usual it would
have to be defined if called directly.

-- Jörg Barfurth

To comp.std.c++: should this be considered for the distant next version of
the standard ?
What do you think ?

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]


[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]


acu...@my-deja.com

unread,
Nov 18, 1999, 3:00:00 AM11/18/99
to
In article <memo.1999111...@btinternet.com>,
bran...@cix.co.uk wrote:

> acu...@my-deja.com () wrote:
> > > 2) Why _can't_ you have a virtual static function?
> >
> > because "virtual static function" is an oxymoron.
>
> You can have them in Smalltalk.

Then the word "static" has different semantics/connotation in
Smalltalk. Given the "virtual" and "static" definitions in C++, you
can't have "virtual static function" period -- unless you change the
semantics of virtual and static in this particular context just to
confuse people more. This is a simple logic assertion...

A.


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Harris

unread,
Nov 19, 1999, 3:00:00 AM11/19/99
to
acu...@my-deja.com () wrote:
> Then the word "static" has different semantics/connotation in
> Smalltalk. Given the "virtual" and "static" definitions in C++, you
> can't have "virtual static function" period -- unless you change the
> semantics of virtual and static in this particular context just to
> confuse people more.

It sounds more like circular reasoning to me, and a pedantic attachment to
terminology. I interpreted the question as, "why do static class methods
have the semantics they do in C++?" "Because if they didn't they would be
different" is not a helpful answer. Of course they would be different. C++
would be a different language. The interest is in the benefits and
drawbacks such a difference would entail.


> This is a simple logic assertion...

That's why I posted. Many replies suggested that people were so set in the
current C++ model they were unable to think of any other way the language
could be. In my view, the original question was a good one. It's quite
reasonable to want methods which are attached to classes rather than class
instances but which can still be overridden in subclasses. It is not a
logical impossibility. To attack the terminology with which the question
was asked is to miss the point.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

J.Barfurth

unread,
Nov 19, 1999, 3:00:00 AM11/19/99
to

<acu...@my-deja.com> schrieb in im Newsbeitrag:
8128i5$n84$1...@nnrp1.deja.com...
> In article <memo.1999111...@btinternet.com>,

> Then the word "static" has different semantics/connotation in
> Smalltalk. Given the "virtual" and "static" definitions in C++, you
> can't have "virtual static function" period -- unless you change the
> semantics of virtual and static in this particular context just to

> confuse people more. This is a simple logic assertion...

"static" has (too) many meanings in C++ already. But:
- In the given context (class members), "static" means "per class" as
opposed to "per object".
- In that same context "virtual" means "can vary with the dynamic type of
the object".
But one feature that most certainly changes with the dynamic type of an
object is the class it belongs to ;-o
So to me the concepts seem to be compatible (they may not be fully
orthogonal though).

IMHO there is no real change in semantics necessary to combine those
concepts.
For elaboration see my recent post 'static virtual ?' in this thread.

-- Jörg Barfurth

sirwi...@my-deja.com

unread,
Nov 19, 1999, 3:00:00 AM11/19/99
to
In article <3832C603...@lucent.com>,

Michiel Salters <sal...@lucent.com> wrote:
> merl...@my-deja.com wrote:
>
> > In article <80rvhs$4tk$1...@nnrp1.deja.com>, merl...@my-deja.com
wrote:
>
> Aha - you think you can only override virtual functions. This isn't
> true, e.g.
> class Bnv {
> void foo() { cout << "B's non-virtual foo\n"; }
> }
> class Dnv {
> void foo() { cout << "D's non-virtual foo\n"; }
> }
>
> is perfecly legal

But is overloading (in addition to hiding), not overriding. This is a
sublte, but important distinction.

>, and with
>
> Bnv b; Dnv d;
> b.foo();
> d.foo();
>
> The result is:
>
> B's non-virtual foo
> D's non-virtual foo
>
> The surprise is in
> Bnv *base;
> base=&b; base->foo();
> base=&d; base->foo()
>
> which prints.
>
> B's non-virtual foo
> B's non-virtual foo
>
> Because *base is a Bnv.

And because you overloaded the method instead of overriding it.

> Yes -and if you leave off the virtual, the above program is just
fine, and
> will work as expected. Virtual is only needed if you call a function
on
> an object of a derived class via a pointer to the base class part.
> You can't use pointers to a base class to call static member
functions.

All very true, but to do what he wants I *think* you need the ability
to call the "static" method through the object pointer... i.e. you
don't know the type to call the method. Another use for the typeof
keyword ;) *chuckles*. Sorry, I really don't want to open that can of
worms, at least not in this thread... if anyone wants to dwell on this
after I mentioned it, please, start a new thread.

Valentin Bonnard

unread,
Nov 19, 1999, 3:00:00 AM11/19/99
to
First, a technical comment: a conforming implementation for
type_info::name () is { return ""; }, so your code isn't
strictly portable.

I haven't read all your article but I will summarize my
position anyway: when faced with an apparent multiple
dispatch (MD) problem:

- check if MD is really wanted: to we really want to
parametrize an operation @ over A and B, or if there
are f, g and h such that:

h (f (A), g (B)) = A @ B

f resp. g is parametrized on only one type (A resp. B);
example:

stream_type << object_type

is really

put_chars_into_buffer (get_buffer(stream_type),
to_chars(object_type))

- check if MD has to occur at runtime or compile-time, with
template (and type-safe code...); in the lastest case, overloading
will solve the problem

- will nested switch/if handle it ?

- do we really want polymorphism on both arguments ?

To me MD is a complicated thing, and I think it's often made
of smaller units. Before parametrizing anything, I split MD
into these simplier pieces. It's difficult to reason about
MD in general because we don't know a-priori how to split into
smaller pieces an arbitrary MD problem.

--

Valentin Bonnard

Stefan Seefeld

unread,
Nov 19, 1999, 3:00:00 AM11/19/99
to
Dave Harris wrote:

>
> acu...@my-deja.com () wrote:
> > Then the word "static" has different semantics/connotation in
> > Smalltalk. Given the "virtual" and "static" definitions in C++, you
> > can't have "virtual static function" period -- unless you change the
> > semantics of virtual and static in this particular context just to
> > confuse people more.
>
> It sounds more like circular reasoning to me, and a pedantic attachment to
> terminology. I interpreted the question as, "why do static class methods
> have the semantics they do in C++?" "Because if they didn't they would be
> different" is not a helpful answer. Of course they would be different. C++
> would be a different language. The interest is in the benefits and
> drawbacks such a difference would entail.

the short answer is probably efficiency and static type safety.

Stefan
_______________________________________________________

Stefan Seefeld
Departement de Physique
Universite de Montreal
email: seef...@magellan.umontreal.ca

_______________________________________________________

...ich hab' noch einen Koffer in Berlin...

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Harris

unread,
Nov 20, 1999, 3:00:00 AM11/20/99
to
Bonn...@wanadoo.fr (Valentin Bonnard) wrote:
> First, a technical comment: a conforming implementation for
> type_info::name () is { return ""; }, so your code isn't
> strictly portable.

I noticed both Scott Meyers, and Stroustrup (in C++PL $15.4.4.1) use the
name rather then type_info::before(). Why is this? What is the before()
method for if not for use in maps etc?

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

merl...@my-deja.com

unread,
Nov 20, 1999, 3:00:00 AM11/20/99
to
In article <80vjul$ore$1...@nnrp1.deja.com>, acu...@my-deja.com wrote:
> In article <80t00f$ueg$1...@nnrp1.deja.com>,
> merl...@my-deja.com wrote:
> > What I meant by "virtual static function" was a virtual function
> > (e.g., one that can legally be redefined through the C++
> > inheritance mechanisms) that is also static (e.g., one that does
> > *not* need access to *this and thus can be called as a class
> > method). Thus, I'd like to be able to write something like:

[ sample code sacrificed to appease the gods of bandwidth ]

> > My question then is -- I know I can't do this, but why?
> get rid of the virtual keyword, add necessary semicolons at the end of
> class definitions, change void main to int main, you've got a legal
> code that behaves exactly the way you wanted. What's the problem?

Because C++ static functions don't work the way I want when called
through a base class pointer. The original idea was to use the same
getKey() function to both initialize the collision map (when called
statically) and to look up collision-processing function pointers (when
called virtually through a GameObject pointer). However, there's no
mechanism in C++ to do what I want.

merl...@my-deja.com

unread,
Nov 20, 1999, 3:00:00 AM11/20/99
to
In article <813u00$sed$1...@nnrp1.deja.com>, sirwi...@my-deja.com wrote:
> In article <3832C603...@lucent.com>,
> Michiel Salters <sal...@lucent.com> wrote:

[ chalupa ... err, quoted material ... snipped ]

> > You can't use pointers to a base class to call static member
> functions.
> All very true, but to do what he wants I *think* you need the ability
> to call the "static" method through the object pointer... i.e. you
> don't know the type to call the method.

Exactly.

> Another use for the typeof keyword ;) *chuckles*.

Actually, no. When calling the method through the object pointer, I
*need* to use the virtual overriding mechanism. Ideally, I'd like some
way of doing the following (using the classes I defined in my original
post, where Spaceship inherits from GameObject, MilitarySpaceship
inherits from Spaceship, and Torpedo inherits from MilitarySpaceship):

// Static dispatch
Spaceship::getKey(); // returns "Spaceship"
Torpedo::getKey(); // returns "Torpedo"

// Dispatch via base class pointer
GameObject* p1 = new MilitarySpaceship();
GameObject* p2 = new Torpedo();
p1->getKey(); // returns "Spaceship"
p2->getKey(); // returns "Torpedo"

Ian McCulloch

unread,
Nov 21, 1999, 3:00:00 AM11/21/99
to

merl...@my-deja.com wrote in message <816dd0$ia8$1...@nnrp1.deja.com>...

>In article <813u00$sed$1...@nnrp1.deja.com>, sirwi...@my-deja.com wrote:
>> In article <3832C603...@lucent.com>,
>> Michiel Salters <sal...@lucent.com> wrote:
>
>[ chalupa ... err, quoted material ... snipped ]
>
>> > You can't use pointers to a base class to call static member
>> functions.
>> All very true, but to do what he wants I *think* you need the ability
>> to call the "static" method through the object pointer... i.e. you
>> don't know the type to call the method.
>
>Exactly.

I'm slightly confused here, is the behaviour you are looking for equivalent
to a virtual function that delegates to the static? eg,

struct Base
{
static void MyStatic() { }

virtual void CallMyStatic() { MyStatic(); }
}

struct Derived
{
static void MyStatic() { } // hide Base::MyStatic() - or call it
something different

// override Base::CallMyStatic to use Derived::MyStatic
virtual void CallMyStatic() { MyStatic(); }
}

// Usage:

int main()
{
Base* pBase = new Base();
Base* pDerived = new Derived();

pBase->CallMyStatic(); // equivalent to Base::MyStatic();
pDerived->CallMyStatic(); // equivalent to Derived1::MyStatic();
}

The problem being that whenever you write a new static, you also have to
override the virtual.

Another way of doing this would be a map from type_info to function
pointers. Then you could do something like

CallMyStatic(typeid(MyPointer))();

This is probably conceptually closer to what you're looking for, but a bit
more cumbersome to implement, since you need some way of registering the
type_info of each candidate object. It does have the advantage that you
could use templates to let the compiler do normal overload resolution on the
static, eg

template <class T>
struct MyDispatcher
{
static void Dispatch() { T::MyStatic(); }
};

Then the mapping is typeid(class) --> MyDispacher<class> for each class that
is used.

Cheers,
Ian McCulloch

acu...@my-deja.com

unread,
Nov 21, 1999, 3:00:00 AM11/21/99
to
In article <816c25$hee$1...@nnrp1.deja.com>,

merl...@my-deja.com wrote:
> In article <80vjul$ore$1...@nnrp1.deja.com>, acu...@my-deja.com wrote:
> > In article <80t00f$ueg$1...@nnrp1.deja.com>,
> > merl...@my-deja.com wrote:
> > > What I meant by "virtual static function" was a virtual function
> > > (e.g., one that can legally be redefined through the C++
> > > inheritance mechanisms) that is also static (e.g., one that does
> > > *not* need access to *this and thus can be called as a class
> > > method). Thus, I'd like to be able to write something like:
>
> [ sample code sacrificed to appease the gods of bandwidth ]
>
> > > My question then is -- I know I can't do this, but why?
> > get rid of the virtual keyword, add necessary semicolons at the end
of
> > class definitions, change void main to int main, you've got a legal
> > code that behaves exactly the way you wanted. What's the problem?
>
> Because C++ static functions don't work the way I want when called
> through a base class pointer. The original idea was to use the same
> getKey() function to both initialize the collision map (when called
> statically) and to look up collision-processing function pointers
(when
> called virtually through a GameObject pointer). However, there's no
> mechanism in C++ to do what I want.

Ah, now I understand you meant: you want to use the same name for both
virtual and static function. spoiled by name overloading, don't you?
that's just a minor convenience, but it would another yet another rule
for compilers to check that your trivial "virtual static function"
doesn't use any members. You can always use:

static const string& staticKey();
virtual const string& getKey() const { return staticKey(); }

The above code is perfectly legal and portable -- that was my point.

A.

Andrei Alexandrescu

unread,
Nov 21, 1999, 3:00:00 AM11/21/99
to
Dave Harris <sc...@btinternet.com> wrote in message
news:memo.19991120...@btinternet.com...

> Bonn...@wanadoo.fr (Valentin Bonnard) wrote:
> > First, a technical comment: a conforming implementation for
> > type_info::name () is { return ""; }, so your code isn't
> > strictly portable.
>
> I noticed both Scott Meyers, and Stroustrup (in C++PL $15.4.4.1) use
the
> name rather then type_info::before(). Why is this? What is the
before()
> method for if not for use in maps etc?

Scott acknowledged the comparison based upon type_info::name() to be a
bug. It's got to be on his errata pages.

Basically you cannot do anything of interest with type_info::name(),
other than outputting it to a debug console. I guess that even making
it part of error messages that are seen by the client is not very
adequate.


Andrei

merl...@my-deja.com

unread,
Nov 21, 1999, 3:00:00 AM11/21/99
to
In article <817u8s$i7j$1...@nnrp1.deja.com>, acu...@my-deja.com wrote:
> Ah, now I understand you meant: you want to use the same name for both
> virtual and static function.

Not quite; I want to have *one* function that is *both* virtual and
static. If I have two functions, then someday some nitwit maintenance
programmer will get one right and one wrong. If there's only one
function, the odds of an error are reduced; should an error occur, at
least the code will be consistent.

> You can always use:
> static const string& staticKey();
> virtual const string& getKey() const { return staticKey(); }
> The above code is perfectly legal and portable -- that was my point.

No question about being portable, and I agree that it's legal C++.
However, now that same nitwit maintenance programmer can call the static
method through a base pointer, which will result in legal but erroneous
code as follows:

struct GameObject
{
virtual const string& getKey() const = 0;
};

struct Spaceship: public GameObject
{
static const string& getStaticKey() { return "Spaceship"; }
virtual const string& getKey() const { return getStaticKey(); }
};

struct MilitarySpaceship: public Spaceship
{
// no override of getKey(), thus inherits Spaceship::getKey();
};

struct Torpedo: public MilitarySpaceship
{
static const string& getStaticKey() { return "Torpedo"; }
virtual const string& getKey() const { return getStaticKey(); }
};

int main()
{
Spaceship *ps = new Torpedo();
ps->getKey(); // invokes Torpedo::getKey()
ps->getStaticKey(); // invokes Spaceship::getStaticKey()

if ( ps->getKey() == ps->getStaticKey() )
// the programmer probably expects this
cout << "Same key" << endl;
else
// but the programmer is going to get this
cout << "Keys differ" << endl;
}

Hopefully, the nitwit maintenance programmer will only make this mistake
once. OTOH, I'd much prefer not to give him the opportunity to screw
up in the first place.

--
Edmund Schweppe aka merl...@my-deja.com
Blissfully free of official positions

Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Harris

unread,
Nov 22, 1999, 3:00:00 AM11/22/99
to
merl...@my-deja.com () wrote:
> Not quite; I want to have *one* function that is *both* virtual and
> static. If I have two functions, then someday some nitwit maintenance
> programmer will get one right and one wrong. If there's only one
> function, the odds of an error are reduced; should an error occur, at
> least the code will be consistent.

Have you considered using a Prototype Pattern instead? For example:

namespace GameObjectPrototypes {
static const SpaceShip spaceShip;
static const Asteroid asteroid;
static const Torpedo torpedo;
//...
}

void RegisterCollisionTypes() {
using namespace GameObjectPrototypes;

RegisterCollisionType( spaceShip, asteroid, &shipAsteroid );
RegisterCollisionType( spaceShip, torpedo, &shipTorpedo );
//...
}

void RegisterCollisionType( const GameObject &a, const GameObject &b,
CollisionFunc f ) {
collisionMap[ make_pair( a.GetKey(), b.GetKey() ) ] = f;
}

In other words, construct an entire spaceship and call its virtual
function. That way you don't need the static function at all. Arguably it
is simpler and less error prone to create an entire object that to fiddle
about trying to share strings between individual functions.

Admittedly the creation could be expensive, but you only need one object
of each type. The namespace is a reflection of your GameObjectKeys, but if
RegisterCollisionTypes is the only place the static keys are used you
could make the objects local variables of that function.

On the other hand, keeping the prototypes around might be useful for other
things. For example, you might want to check the size of asteroids by
comparing them with a prototypical bigAsteroid. Or create new objects by
cloning the existing, pristine ones - the prototypes would act as a
repository for various game object parameters.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Hendrik Schober

unread,
Nov 22, 1999, 3:00:00 AM11/22/99
to
<merl...@my-deja.com> wrote:.

> In article <817u8s$i7j$1...@nnrp1.deja.com>, acu...@my-deja.com wrote:
> [...]

> However, now that same nitwit maintenance programmer can call the static
> method through a base pointer, which will result in legal but erroneous
> code as follows:
> [...]

Make the static method private.

Schobi

Francis Glassborow

unread,
Nov 22, 1999, 3:00:00 AM11/22/99
to
In article <819rb6$pes$1...@nnrp1.deja.com>, merl...@my-deja.com writes

>Not quite; I want to have *one* function that is *both* virtual and
>static. If I have two functions, then someday some nitwit maintenance
>programmer will get one right and one wrong. If there's only one
>function, the odds of an error are reduced; should an error occur, at
>least the code will be consistent.

If the virtual one just forwards to the static version it is pretty hard
to see how an employable programmer could get it wrong.


Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Andrei Alexandrescu

unread,
Nov 22, 1999, 3:00:00 AM11/22/99
to
Valentin Bonnard <Bonn...@wanadoo.fr> wrote in message
news:3834EE...@wanadoo.fr...

> - will nested switch/if handle it ?

One of the purposes of MD is to avoid using nested switches and ifs, which
are a maintenance nightmare.


Andrei

sirwi...@my-deja.com

unread,
Nov 22, 1999, 3:00:00 AM11/22/99
to
In article <816dd0$ia8$1...@nnrp1.deja.com>,

merl...@my-deja.com wrote:
> In article <813u00$sed$1...@nnrp1.deja.com>, sirwi...@my-deja.com
wrote:
> > In article <3832C603...@lucent.com>,
> > Michiel Salters <sal...@lucent.com> wrote:
>
> [ chalupa ... err, quoted material ... snipped ]
>
> > > You can't use pointers to a base class to call static member
> > functions.
> > All very true, but to do what he wants I *think* you need the
ability
> > to call the "static" method through the object pointer... i.e. you
> > don't know the type to call the method.
>
> Exactly.
>
> > Another use for the typeof keyword ;) *chuckles*.
>
> Actually, no. When calling the method through the object pointer, I
> *need* to use the virtual overriding mechanism.

[snipped the rest of the explanation and code]

I understand this. Quite often, when the non-existant typeof keyword
is discussed it is unclear as to whether it would/should return the
static type or the runtime type of an object. The general consensus is
that it would be statically typed, as is sizeof, but if it were runtime
typed instead it would suffice for this particular problem. There are
definate reasons why runtime typing would be problematic to implement
at best, thus the wink and chuckle in my remark.


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

sirwi...@my-deja.com

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <38358144...@magellan.umontreal.ca>,

Stefan Seefeld <seef...@magellan.umontreal.ca> wrote:
> > It sounds more like circular reasoning to me, and a pedantic
attachment to
> > terminology. I interpreted the question as, "why do static class
methods
> > have the semantics they do in C++?" "Because if they didn't they
would be
> > different" is not a helpful answer. Of course they would be
different. C++
> > would be a different language. The interest is in the benefits and
> > drawbacks such a difference would entail.
>
> the short answer is probably efficiency and static type safety.

I'm not sure I see this. Leave the virtual keyword off and statics
would be EXACTLY as they are today. Put the virtual keyword on and the
static call would be as efficient and type safe as other virtual
methods are (typesafety wouldn't be sacrificied in any way, since the
type is deduced from either the reference/pointer being called through
same as a normal virtual method is, or would be deduced from the
explicit name given for "standard static method calls", and the call
would be "less efficient" by one level of indirection when called
through a pointer/reference). Under this scenario, the ONLY difference
between a static method and a non-static method is that static methods
would not be passed a this pointer. I realize C++ doesn't do this and
I can see why it wouldn't have been thought of to begin with, but I
don't see any reason the semantics couldn't be changed.

merl...@my-deja.com

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <dw2SgOAl...@robinton.demon.co.uk>, Francis Glassborow

<fran...@robinton.demon.co.uk> wrote:
> In article <819rb6$pes$1...@nnrp1.deja.com>, merl...@my-deja.com writes
> >Not quite; I want to have *one* function that is *both* virtual and
> >static. If I have two functions, then someday some nitwit maintenance
> >programmer will get one right and one wrong. If there's only one
> >function, the odds of an error are reduced; should an error occur, at
> >least the code will be consistent.
> If the virtual one just forwards to the static version it is pretty
> hard to see how an employable programmer could get it wrong.

Having recently gone through that wonderful exercise known as Y2K
remediation, any illusions that I might have had of a minimal competency
level for "employable" programmers have been eternally shattered. (I'm
reminded of one MS-Windows application which had *three* calendar-based
date entry dialogs. The dialogs looked the same and had the same
functionality; however each dialog was individually coded and used a
different set of global variables to pass dates to and from its caller.)

If a compiler will let a programmer do something, sooner or later some
programmer will do it.

--
Edmund Schweppe aka merl...@my-deja.com
Blissfully free of official positions

AllanW

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <memo.1999111...@btinternet.com>,
bran...@cix.co.uk wrote:
> acu...@my-deja.com () wrote:
> > > 2) Why _can't_ you have a virtual static function?

Because in C++, "Virtual functions" means "look up the exact
address of the function at runtime, based on the dynamic type
of the object" and "Static functions" means "a function which
does not use an instance of an object." Since there is no
instance to get the dynamic type of, there is no virtual table
to use.

> You can have them in Smalltalk.
>

> The Smalltalk approach is to reify classes at runtime. That is,
> for each compile-time class there is a runtime object which
> represents it. The static variables become instance variables
> of that object. Static functions become ordinary functions, and
> the class object forms the "this" pointer and stores the vtbl
> (or whatever) for virtual functions.

Very clever.

> For this to be useful, different classes must have different
> instance variables and so must be different types. Call the
> class of a class the "metaclass"; we're saying different
> classes must have different metaclasses. And then it all
> works. A static method of the base class can be overridden
> by a static method of the derived class.

Excellent! Well, problem solved.

> It is beautiful, simple and powerful. It's also fairly hard to
> get your head around, to teach etc. It involves extra runtime
> overhead - eg we've added at least 2 runtime objects to the
> system for every class.

That shouldn't be too much of a drawback. Smalltalk users want
decent performance, but my understanding is that they're not too
concerned about the absolute theoretical maximum.

> There may be issues of compatibility with C, I don't know.

How strange. Why would Smalltalk users be worried about
compatibility with C?

> Stroustrup briefly mentions the idea in D&E, under "Meta-Objects"
> $14.2.8.1. He adds type-checking issues to the list of problems.

Well, anyway, since the solution is to write the program in
Smalltalk, you should probably continue this thread in a different
newsgroup. Is there a comp.std.smalltalk? I suggest going back to
the original message and re-posting it; I would have cross-posted
this message to both groups, but it probably doesn't have enough
context to be meaningful to readers of that group.

--
All...@my-deja.com is a "Spam Magnet," never read.
Please reply in newsgroups only, sorry.

Lorenzo Bettini

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to

"J.Barfurth" wrote:
> > 2) Why _can't_ you have a virtual static function? There must be a good
> > reason, but I have no clue as to what that good reason is.

In my opinion I think the reason is quite simple:

a static method is not passed "this", and the (run time) type of this is
used to select a virtual method.

Lorenzo

--
+-----------------------------------------------------+
| Lorenzo Bettini ICQ# lbetto, 16080134 |
| PhD student in Computer Science |
| Florence - Italy |
| Home Page : http://w3.newnet.it/bettini |
| http://infostud.dsi.unifi.it/~bettini |
| Mail Home : lorenzo...@penteres.it |
| Mail University : bet...@dsi.unifi.it |
| http://www.mokabyte.it Java on line journal |
| http://rap.dsi.unifi.it/xklaim XKlaim language |
+-----------------------------------------------------+

sirwi...@my-deja.com

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <s3g309...@news.supernews.com>,

"Andrei Alexandrescu" <andre...@hotmail.com> wrote:
> Dave Harris <sc...@btinternet.com> wrote in message
> news:memo.19991120...@btinternet.com...
> > Bonn...@wanadoo.fr (Valentin Bonnard) wrote:
> > > First, a technical comment: a conforming implementation for
> > > type_info::name () is { return ""; }, so your code isn't
> > > strictly portable.
> >
> > I noticed both Scott Meyers, and Stroustrup (in C++PL $15.4.4.1) use
> the
> > name rather then type_info::before(). Why is this? What is the
> before()
> > method for if not for use in maps etc?
>
> Scott acknowledged the comparison based upon type_info::name() to be a
> bug. It's got to be on his errata pages.

It is. From http://www.aristeia.com/BookErrata/mec++-errata.html:

(Hoping the cut and paste works well...)

"When setting up collision maps, it would be safer to avoid the use of
literal strings like "SpaceShip". Instead, the maps should be
initialized via calls to typeid. That way it would be harder to
accidently use the wrong string for a class name. Furthermore, the
standard makes it clear that type_info::name can return just about
anything, so there's no portable use for the results of that function.
The map-related typedefs for the non-member solution of pp. 244-248
should really be these:
typedef void (*HitFunctionPtr)(GameObject&, GameObject&);
typedef map<pair<const type_info*, const type_info*>,
HitFunctionPtr> HitMap;"

It should also be noted that even on implementations where the
type_info::name approach might work it would be less efficient because
of the string comparisons.

Alexei Zakharov

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
Ray Tayek <rta...@netcom.com> wrote in message
news:MPG.129db8f51...@news.pacbell.net...
...
> In this case, we are actually using a triple dispatch, because we have
> two
> types to resolve. The first dispatch is the virtual Attacks function
> which
> resolves the type of the object upon which Attacks is called. The second
> dispatch is the virtual Accept function which resolves the type of the
> object passed into Attacks. Now that we know the type of both objects,
> we
> can call the appropriate global function to calculate the Attack. This
> is done by the third and final dispatch to the Visit function.
> */
> interface Monster
> {
> Monster attacks(final Monster monster);
> void accept(MonsterVisitor visitor);
> }
> class Orc implements Monster
> {
> public Monster attacks(final Monster monster)
> {
> OrcVisitor visitor=new OrcVisitor(this);
> monster.accept(visitor);
> return visitor.monster();
> }
> public void accept(MonsterVisitor visitor)
> { visitor.visit(this); } // visit Orc
> }
> class Dragon implements Monster
> {
> public Monster attacks(final Monster monster)
> {
> DragonVisitor visitor=new DragonVisitor(this);
> monster.accept(visitor);
> return visitor.monster();
> }
> public void accept(MonsterVisitor visitor)
> { visitor.visit(this); } // visit Dragon
> }
> // visitors.
> interface MonsterVisitor
> {
> abstract public void visit(Orc orc);
> abstract public void visit(Dragon dragon);
> }
> class DragonVisitor implements MonsterVisitor
> {
> DragonVisitor(final Dragon dragon)
> { this.dragon=dragon; }
> public void visit(Orc orc)
> { monster=Interaction.attacks(dragon,orc); }
> public void visit(Dragon dragon)
> { monster=Interaction.attacks(this.dragon,dragon); }
> Monster monster() {return monster; }
> private Monster monster;
> private final Dragon dragon;
> }
> class OrcVisitor implements MonsterVisitor
> {
> OrcVisitor(final Orc orc)
> { this.orc=orc; }
> public void visit(Orc orc)
> { monster=Interaction.attacks(this.orc,orc); }
> public void visit(Dragon dragon)
> { monster=Interaction.attacks(orc,dragon); }
> Monster monster() {return monster; }
> private Monster monster;
> private final Orc orc;
> }
> // interactions
> class Interaction
> {
> static Monster attacks(final Dragon d,final Dragon d2)
> { System.out.print(d+" vs "+d2); return d; }
> static Monster attacks(final Dragon d,final Orc o)
> { System.out.print(d+" vs "+o); return d; }
> static Monster attacks(final Orc o,final Dragon d)
> { System.out.print(o+" vs "+d); return d; }
> static Monster attacks(final Orc o,final Orc o2)
> { System.out.print(o+" vs "+o2); return o; }
> }

Hi,

What is the need of using XXXVisitor classes? As far as I consider
everything works well without it making triple dispatch double (C++
variant):

class Orc;
class Dragon;

class Monster
{
protected:
virtual void accept( Orc & ) = 0;
virtual void accept( Dragon & ) = 0;

public:
virtual void attacks( Monster & ) = 0;
};

class Orc : public Monster
{
protected:
virtual void accept( Orc & orc )
{
// orc vs orc
}
virtual void accept( Dragon & dragon )
{
// orc vs dragon
}

public:
virtual void attacks( Monster & monster )
{
monster.accept( *this );
}
};

class Dragon: public Monster
{
protected:
virtual void accept( Orc & orc )
{
// dragon vs orc
}
virtual void accept( Dragon & dragon )
{
// dragon vs dragon
}

public:
virtual void attacks( Monster & monster )
{
monster.accept( *this );
}
};

sirwi...@my-deja.com

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <dw2SgOAl...@robinton.demon.co.uk>,
Francis Glassborow <fran...@robinton.demon.co.uk> wrote:
> In article <819rb6$pes$1...@nnrp1.deja.com>, merl...@my-deja.com writes
> >Not quite; I want to have *one* function that is *both* virtual and
> >static. If I have two functions, then someday some nitwit maintenance
> >programmer will get one right and one wrong. If there's only one
> >function, the odds of an error are reduced; should an error occur, at
> >least the code will be consistent.
>
> If the virtual one just forwards to the static version it is pretty
hard
> to see how an employable programmer could get it wrong.

If the destructor just deletes allocated memory it is pretty hard to
see how an employable programmer could get it wrong *chuckles*.
Programmers are human. Forget this and you're in major trouble.


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

sirwi...@my-deja.com

unread,
Nov 23, 1999, 3:00:00 AM11/23/99
to
In article <81b68i$bf0$1...@news1.transmedia.de>,

"Hendrik Schober" <h.sc...@nospam.callassoftware.com> wrote:
> <merl...@my-deja.com> wrote:.
> > In article <817u8s$i7j$1...@nnrp1.deja.com>, acu...@my-deja.com wrote:
> > [...]
> > However, now that same nitwit maintenance programmer can call the
static

> > method through a base pointer, which will result in legal but
erroneous
> > code as follows:
> > [...]
>
> Make the static method private.

Now you can't call the static method with out an instance pointer, so
basically it's not static any more. I'll illustrate what's wanted here:

class A
{
public:
static virtual print() { cout << "I'm an A!" << endl; }
};

class B : public A
{
public:
static virtual print() { cout << "I'm a B!" << endl; }
};

int main()
{
A a;
B b;
A* c = &b;
a.print();
b.print();
c->print();
A::print();
B::print();
}

This should output:

I'm an A!
I'm a B!
I'm a B!
I'm an A!
I'm a B!

With out the "static virtual" this program is totally valid except for
the line "c->print();", which will output "I'm an A!" instead of the
wanted "I'm a B!". This is possible, it's just not what we have today.

Micha Berger

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to
On 23 Nov 1999 03:59:26 -0500, sirwi...@my-deja.com wrote:
:> the short answer is probably efficiency and static type safety.

: I'm not sure I see this. Leave the virtual keyword off and statics
: would be EXACTLY as they are today.

Slight detour...

As languages such as Dylan (which has multiple dispatch) and Eiffel show,
every function could, in theory, be "virtual" with no efficiency loss.

What is required is post-likage optimization -- a lack C++ inherited from C.
The extra indirection required for a v-table can be optimized out when you
find that it's happens to be constant for every invocation in this build.
C++ already has enough to know when to optimize away taking a "this" pointer
as a parameter. The problem is in informing a calling function in another
file not to pass one, that this optimization has occured.

-mi

--
Micha Berger (973) 916-0287 MMG"H for 23-Nov-99: Shelishi, Vayishlach
mi...@aishdas.org A"H
http://www.aishdas.org Pisachim 73a
For a mitzvah is a lamp, and the Torah its light.

John Panzer

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to
sirwi...@my-deja.com wrote:

Hmmm.... looking at the map<> typedef above, it seems to me that there is no
comparison function defined for the pair<> keys. Is there a standard
operator< or less_than comparator for pair<> objects? I don't see a custom
comparator in Scott's errata. The default under SGI STL is to compare the
"first" field, then the "second" field if necessary, which seems reasonable
but may not be part of the Standard. But even if it is, I think there's a
problem:

The address returned by &typeid(T) isn't guaranteed to be unique within a
program (_The C++ Programming Language_, 3rd Ed, p. 415). So comparing
pointers to see if two types are the same is not guaranteed to work, and the
map above really needs a custom comparator to be correct. Or am I missing
something?

John Panzer

Gerhard Menzl

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to

What would you expect to be the output if you replace main() with:

void f (A* pa)
{
pa->print ();
}

?

Your idea works only as long as the dynamic type of A* is also known
statically - unless you replace the C++ compilation model by something
smarter. Do you advocate such an innovation, or would you be content if static
virtuals merely work in special cases such as the above?

Gerhard Menzl

Peter Dimov

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to
AllanW <all...@my-deja.com> wrote in message
news:81cfeo$lrb$1...@nnrp1.deja.com...
> > acu...@my-deja.com () wrote:
> > > > 2) Why _can't_ you have a virtual static function?
>
> Because in C++, "Virtual functions" means "look up the exact
> address of the function at runtime, based on the dynamic type
> of the object" and "Static functions" means "a function which
> does not use an instance of an object." Since there is no
> instance to get the dynamic type of, there is no virtual table
> to use.

Not exactly. 'virtual' means "look up the exact address of the function at
runtime, _if_ called through a pointer or a reference." This is a
requirement for the caller.

'static' means "the function does not use 'this'." This is a requirement for
the function body. There is no conflict here.

A hypothetical virtual static function, therefore, will use dynamic dispatch
when called through a pointer or reference, but will also be callable
directly using the T::f() notation.

Under the current C++ rules I'd simulate the latter with:

template<class T> void call_virtual_static(void (T::*pmf)())
{
(T().*pmf)();
}

replacing T::f(); with call_virtual_static(&T::f);

Another possibility might be

template<class T> void call_virtual_static(void (T::*pmf)())
{
static T t;
(t.*pmf)();
}

if the constructors/destructors are expensive.

Full code follows:

#include <iostream>

struct A
{
virtual void f() { std::cout << "A::f();\n"; }
};

struct B: public A
{
virtual void f() { std::cout << "B::f();\n"; }
};

template<class T> void call_virtual_static(void (T::*pmf)())
{
static T t;
(t.*pmf)();
}

B b;
A* pa = &b;

int main()
{
pa->f();
call_virtual_static(&A::f);
call_virtual_static(&B::f);
return 0;
}

--
Peter Dimov
Multi Media Ltd.

J.Barfurth

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to

Francis Glassborow <fra...@robinton.demon.co.uk> schrieb in im Newsbeitrag:
dw2SgOAl...@robinton.demon.co.uk...

> In article <819rb6$pes$1...@nnrp1.deja.com>, merl...@my-deja.com writes
> >Not quite; I want to have *one* function that is *both* virtual and
> >static. If I have two functions, then someday some nitwit maintenance
> >programmer will get one right and one wrong. If there's only one
> >function, the odds of an error are reduced; should an error occur, at
> >least the code will be consistent.
>
> If the virtual one just forwards to the static version it is pretty hard
> to see how an employable programmer could get it wrong.

What is _the_ static function. The condition was that the virtual function
(for a given object) does the same thing as the static function for the same
type. This requirement may be necessary in some designs that mix templates
and polymorphism.
If a maintenance programmer changes an existing class, one can indeed hope
that he will not change the simple forwarding virtual (although it still
remains only a hope).
The trouble comes when adding a new derived class. Here the requirement
could be missed or one of the functions' names misspelled. It is easy to
only override one of the functions without keeping the other in sync.

--Jörg Barfurth

Dave Harris

unread,
Nov 24, 1999, 3:00:00 AM11/24/99
to
A.S.Za...@inp.nsk.su (Alexei Zakharov) wrote:
> What is the need of using XXXVisitor classes? As far as I consider
> everything works well without it making triple dispatch

Your version hardwires the action. If all you have is "attacks", that's
fine, but if you have other kinds of interaction you will have to update
your hierarchy more often. In effect you're replaced the triple <Monster,
Monster, Interaction> with <Monster, Monster>.

Admittedly the original code adds "attacks" methods all over the place,
which seems to defeat the object. I think we could avoid that with another
visitor. Something like:

// Other (Java) code as before.

class AttackVisitor implements MonsterVisitor {

public static Monster attack( Monster attacker, Monster victim ) {
AttackVisitor visitor = new AttackVisitor( victim );
attacker.accept( visitor );
return visitor.getResult();
}

private Monster victim;
private Monster result;

public Monster AttackVisitor( Monster victim ) {
this.victim = victim;
}
public getResult() { return result; }

public void visit( Orc attacker ) {
OrcVisitor visitor=new OrcVisitor( attacker );
victim.accept(visitor);
result = visitor.monster();
}

public void visit( Dragon attacker ) {
DragonVisitor visitor=new DragonVisitor( attacker );
victim.accept(visitor);
result = visitor.monster();
}
}

We can now add new routines like attack() without touching the Monster
hierarchy. We need to write N+1 visitors instead. This is a win if the
Monsters need to be stable.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Gregory Knapen

unread,
Nov 25, 1999, 3:00:00 AM11/25/99
to
> A forgotten fact about multiple dispatch:

It tends to breaks encapsulation.

Laguages that support multiple dispatch such as Dylan and CLOS have differnt
mechanism for encapsulation. They use the concept of modules. In these
languages,
records, classes or types (depending on the terminology used by the language)
are
not used as the unit of encapsulation as C++ does. In C++ a class encapsulates
all it's members and defines access rights to them. In Dylan and CLOS the
concepts of type and of encapsulation are orthogonal.

In general, multimethods as they are sometimes called are defined outside of any
class i..e like a function
(in fact this concepts comes from functionnal languages). This is normal since
they don't know which types they are going to be called on. Also to do anything
usefull on the parameters that are passed to the multimethod, it must have
access
to
the members of every parameter.

Greg

J.Barfurth

unread,
Nov 25, 1999, 3:00:00 AM11/25/99
to

Lorenzo Bettini <bet...@gdn.dsi.unifi.it> schrieb in im Newsbeitrag:
383ADFD4...@gdn.dsi.unifi.it...


>
> "J.Barfurth" wrote:
> > > 2) Why _can't_ you have a virtual static function? There must be a
good
> > > reason, but I have no clue as to what that good reason is.
>
> In my opinion I think the reason is quite simple:
>
> a static method is not passed "this", and the (run time) type of this is
> used to select a virtual method.
>
> Lorenzo

Generally you have to select a virtual method first, based on the dynamic
type of the object for which the method is invoked, and only then pass it
'this' as an argument. The type of 'this' may be different from both a
pointer to the static type and the dynamic (most derived) type of the
object. All three pointers could in fact have different values when
static_cast'ed to void*. A 'static virtual' method would just be called
without passing the this pointer. This would even save any overhead
necessary to adjust the value needed for 'this'.

As I pointed out before, there are two cases where we already get this kind
of sychronicity: typeid and class-specific operator delete(well sort of ...
in the latter case).
As opposed to typeid, the object expression used to access a static member
is always evaluated anyway. So we needn't introduce any more special cases
into the language to make 'static virtual' work. Moreover, it wouldn't break
existing code and you won't have to pay it's costs if you don't use it.

With the common 'virtual table' implementation of polymorphic dispatch, each
'static virtual' would just introduce another entry in the virtual table.

Another interesting aspect would be whether taking the address of such a
function (referred to through an lvalue)would yield the address of the final
overrider:
struct A { virtual static A* foo(); }
struct B : A { virtual static B* foo(); void bar(); }// note the
covariant return types

A* p = B::foo();
p->foo(); // calls B::foo
A* (*fp)() = &p->foo; // new syntax necessary
fp(); // calls B::foo
// assert(fp == &B::foo); // incompatible types because of covariance
assert(&p->bar == &B::bar); // works without covariance

I guess it should not be too difficult to implement this: For covariant
return types there would be some kind of stub to adjust the return value.
The function pointer could be copied right from the virtual table, so it
would point to that stub. As the pointer types are then unrelated to the
derived one, a conforming program cannot detect this.

An alternative would be to allow a pointer-to-member function to refer to a
static member function, but IMHO the former is much better.

-- Jörg Barfurth

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

J.Barfurth

unread,
Nov 25, 1999, 3:00:00 AM11/25/99
to

Gregory Knapen <kna...@IRO.UMontreal.CA> schrieb in im Newsbeitrag:
383C3A2D...@IRO.UMontreal.CA...

> > A forgotten fact about multiple dispatch:
>
> It tends to breaks encapsulation.
[...]

> In general, multimethods as they are sometimes called are defined outside
of any
> class i..e like a function
> (in fact this concepts comes from functionnal languages). This is normal
since
> they don't know which types they are going to be called on. Also to do
anything
> usefull on the parameters that are passed to the multimethod, it must have
> access to the members of every parameter.

I disagree. There are uses of functions that take only one object and
(a) use a base class interface only (no extra dispatch needed),
(b) do specialized things to derived objects (some kind of extra dispatch
needed in C++)
and don't need special access to the members of their parameter.

This can be extrapolated to cases where
(c) the function takes more than one parameter and needs to take special
action for certain combinations of dynamic types of their parameters
(multiple dispatch).

The point is, that the objects in question, in their public interface,
support atomic operations (i.e. take care of keeping their invariants
intact).
OTOH there are useful (complex) operations that can be built upon such
primitives. The object should not need to account for all functionality that
can (potentially) be built this way.

<BTW> Nor should it need to know about all (otherwise unrelated) classes it
may be paired with in single dispatch situations. That is the argument in
favor of (acyclic) visitor-based implementations of multiple
dispatch.Implementing multiple dispatch behaviour without a generic
mechanism for multiple dispatch introduces unneccesary coupling.</BTW>

In case (b) the first impetus (in a statically typed language) might be to
simply add a polymorphic method to the class hierarchy to reduce it to case
(a). (Luckily this is more difficult in case (c)).
Skipping the question of cohesion, this would also break encapsulation. The
added member would have unrestricted access to class internals. As the
assumption was that it could be realized without such access, this is not
necessary. It should therefore be avoided.

Therefore encapsulation (as related to cohesion and coupling) is actually an
argument in favor of multiple dispatch dispatching to global[*] functions:
Objects take care of their own state. Functionality (or implicit
relationships) between multiple objects, that does not belong to any single
one of the classes involved, should be built from primitives that respect
encapsulation. Using (non-friend) functions, the compiler can enforce this.

[*] global in this context. Member functions of classes that act upon our
hierarchy from the outside would also qualify.

sirwi...@my-deja.com

unread,
Nov 26, 1999, 3:00:00 AM11/26/99
to
In article <383B3B69...@netscape.com>,

Correct.

> Is there a standard
> operator< or less_than comparator for pair<> objects?

I'm not sure about this, but the answer is irrelavent for Scott's code,
since the "default" would have the problems you point out below.

> I don't see a custom
> comparator in Scott's errata. The default under SGI STL is to
compare the
> "first" field, then the "second" field if necessary, which seems
reasonable
> but may not be part of the Standard. But even if it is, I think
there's a
> problem:
>
> The address returned by &typeid(T) isn't guaranteed to be unique
within a
> program (_The C++ Programming Language_, 3rd Ed, p. 415). So
comparing
> pointers to see if two types are the same is not guaranteed to work,
and the
> map above really needs a custom comparator to be correct. Or am I
missing
> something?

No, you aren't missing anything. Or rather, what you are missing is
yet another mistake (I think). Scott has told me that when he tested
his code he used a specialization of std::less (if this is mentioned in
the errata, I missed it). So, his code for map is correct, but you're
missing the fact that he specialized std::less to work in this
context. The mistake (I *think*) is that the standard prohibits
specializing std::less for any but user defined types, and
std::pair<type_info*, type_info*> uses only implementation defined
types. This topic is being discussed in another thread I started in
this group (titled, I think, "Discussion: type_info"). Instead of
polluting this thread, I'd suggest taking further talk on this subject
to the other thread.


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

sirwi...@my-deja.com

unread,
Nov 26, 1999, 3:00:00 AM11/26/99
to
In article <383B9E49...@sea.ericsson.se>,

You can't tell what the output will be without knowing the type of
object that pa points at. This is no different than a virtual
function. If print were purely a static the compiler, upon seeing the
above code, would say "hmmm... pa points to an A instance so we'll call
A::print". Add our extension of static virtual and the compiler would
say instead "hmmm... pa points to an A instance and A defines print to
be virtual, so look up the method within the virtual table and call
print through the virtual function pointer". The *ONLY difference in
this model between a static and a non-static method is that no "this"
pointer is passed to static methods. The static method may still be
virtual and retain an entry in the v-table, thus allowing calls through
pointers to behave virtually even though no "this" pointer is passed.
This isn't complicated.

> Your idea works only as long as the dynamic type of A* is also known
> statically - unless you replace the C++ compilation model by something
> smarter.

No, static typing is not required, and in fact it's not possible in the
face of polymorphism, which is exactly what we're dealing with here.
As I pointed out above, enabling C++ to do this is trivial. It does
require changing the compilation model, as the addition of any key word
would do, but the foundation of the change is in line with existing
practice, i.e. it behaves little different from normal virtual
invocation. I fail to see why some people continue to want to argue
that this is impossible.

> Do you advocate such an innovation, or would you be content if static
> virtuals merely work in special cases such as the above?

No, I would not be satisfied if it only worked in special cases. Like
all such practices it must be generic to be useful. There is *NO*
reason it must be useable only on special cases, however. A very
simple extrapolation of the current model would suffice admirably.

Scott Meyers

unread,
Nov 28, 1999, 3:00:00 AM11/28/99
to
On 26 Nov 1999 18:19:16 -0500, sirwi...@my-deja.com wrote:
> No, you aren't missing anything. Or rather, what you are missing is
> yet another mistake (I think). Scott has told me that when he tested
> his code he used a specialization of std::less (if this is mentioned in
> the errata, I missed it). So, his code for map is correct, but you're
> missing the fact that he specialized std::less to work in this
> context. The mistake (I *think*) is that the standard prohibits
> specializing std::less for any but user defined types, and
> std::pair<type_info*, type_info*> uses only implementation defined
> types.

Right. Geez, this month has been brutal for MEC++ in this newsgroup.
Sigh.

Here's how I currently plan to word things in the MEC++ errata. I haven't
changed the on-line version yet, because I batch updates to the errata
lists. From an administrative point of view, this is easier for me, and it
also makes it possible for me to make periodic announcements about the
updates to my mailing list (see
http://www.aristeia.com/MailingList/index.htmlfor details):

When setting up collision maps, it would be safer to avoid
the use of literal strings like "SpaceShip". Instead, the
maps should be initialized via calls to typeid. That way it
would be harder to accidently use the wrong string for a
class name. Furthermore, the standard makes it clear
that type_info::name can return just about anything, so
there's no portable use for the results of that function.

That suggests that the map-related typedefs for the


non-member solution of pp. 244-248 should really be these:
typedef void (*HitFunctionPtr)(GameObject&, GameObject&);
typedef map<pair<const type_info*, const type_info*>,
HitFunctionPtr> HitMap;

That's not quite right, however, because the map's
ordering function would be
less<pair<const type_info*, const type_info*> >,
and there's no guarantee that that will (1) exist and
(2) behave in a reasonable fashion. Furthermore,
you're not allowed to define your own version of this
class, because it doesn't use any user-defined types.
You'd therefore have to write a custom comparison class
for the map:
class TICompare { ... }; // "type_info compare"


typedef void (*HitFunctionPtr)(GameObject&, GameObject&);
typedef map<pair<const type_info*, const type_info*>,

HitFunctionPtr, TICompare> HitMap;
Details are left to the reader :-)

With any luck, this is finally correct and standard-conforming. If not, I
know you'll let me know about it...

Scott

--
Scott Meyers, Ph.D. sme...@aristeia.com
Software Development Consultant http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD

Scott Meyers

unread,
Nov 29, 1999, 3:00:00 AM11/29/99
to
On 17 Nov 1999 05:26:21 -0500, acu...@my-deja.com wrote:
> Yes it works. I've been doing this for years. MEC++ item 31 is really
> not state of the art. I've personally thought up at least 2 C++ idioms
> to implement multiple dispatch better (more extendible, more flexible
> and more efficient) than item 31. I do thank Scott for the wonderful
> book, which I got the first edition (in 1996) with Scott's signature.
> It really gave me a good start to think about the more interesting
> aspects of C++.

Thanks. I encourage you to write up your approaches to DD for publication,
either as a magazine article or a web page. That way I could at least add
a link to your material from the "Interesting Comments" part of the MEC++
errata. Since publication of MEC++, I've received a lot of new information
on DD, and I have the outline of an article, "Double Dispatching
Revisited", here that I will probably never have time to write up. (I'd
post the notes, but they're a complete mess. I know, because I was going
to send them to somebody, and I finally decided they were too jumbled to be
meaningful to anybody but me.) Fortunately, I know somebody else (a
frequent contributor to this newsgroup) who is working on a new solution to
the DD problem, so perhaps there will be a new publication in this area in
the not-too-distant future. That would be nice.

FWIW, I'm not wild about designs that require that programmers provide
functions that allow classes to identify themselves, because it's too easy
to forget to add the necessary functions (static, virtual, or typically
both). That's why I prefer type_info-based solutions: the compiler can
provide the necessary information. For example, you could use each class's
type_info object as a class identifier, though you have to be careful,
because implementations are allowed to generate multiple type_info objects
for a given class. (All the objects are required to compare equal,
however.)

DD is an interesting topic. I'm still on the lookout for a solution that
requires minimal recompilation when hierarchies change (naturally,
"minimal" means "none", and Visitor fails this test), that's next to
foolproof to implement for classes involved in the DD (the need to write
class-identification functions would violate this goal), that supports
inheritance (unlike the final design in Item 31), and that localizes
changes to a single function when new classes are added to the DD hierarchy
or hierarchies (for my design, this was the hitmap initialization
function). I suspect it is possible to develop such a design, I just
haven't seen it. Yet.

David R Tribble

unread,
Nov 30, 1999, 3:00:00 AM11/30/99