[MUD-Dev] Re: World-file parsing and RTTI?

Mark Gritter mark at erdos.Stanford.EDU
Wed Feb 10 14:54:17 New Zealand Daylight Time 1999

Joachim Pileborg writes:
> For my MUD, I have a simple yet pretty good (IMHO :) format for world-files.
> A file contains one or more objects (monsters, locations, etc.), and each
> object have a list of properties (key-value pairs).  Right now I have a
> simple lexer in flex that search for certain keywords like "monster",
> "location", "item".  When the parser finds such a keyword I create a new
> instance of that specific C++ class.

I'm not clear here on what the strategy is: you create an instance of
the C++ class with the same name?  What does your table contain as the
"value" associated with a particular keyword?

> My goal for the parser is to make it as flexible as possible, all anyone
> using my code should have to do, is to add a line to a table.
> Now comes the question:  What is the best way to do this?  I have though
> about using RTTI, but to my knowledge there exists no platform/compiler
> independant system.  I could limit the MUD to just be compileable with gcc
> or egcs, but I don't like limiting myself in any way.
> Discussion?

RTTI is part of the standard.  Anything in ISO C++ that g++/egcs can handle 
should be available in commercial compilers by now.  (I hope.)

The "standard" solution to adding this flexiblity is to use factory classes 
rather than trying to use some language feature to create the right type.
I'll sketch an example below of how I've done this on other projects.
(Not MUDs... yet.)

class EntityFactory {
  /* Load an entity from the file.  Subclasses of EntityFactory
   * handle different types.
  virtual Ptr<Entity> loadEntity(istream &in) = 0;

/* Depending on how your parser works, istream &in might be replaced
 * by list<KeywordValuePairs> or something else appropriate for creating
 * the entity.
 * "WorldContext" specifies where the new entity goes in some way.
void Parser::handleKeyword(string keyword, istream &in, WorldContext &world) {
  Ptr<EntityFactory> factory = keywordTable->lookup(keyword);
  Ptr<Entity> newEntity = factory->loadEntity(in):

class MonsterFactory : public EntityFactory {
  virtual Ptr<Entity> loadEntity(istream &in) { ... }

class WeaponFactory : public EntityFactory {
  virtual Ptr<Entity> loadEntity(istream &in) { ... }

void init(void) {
  keywordTable->add("monster", new MonsterFactory());
  keywordTable->add("weapon", new WeaponFactory());

This allows new types to be added by creating the new factory and putting
it in the keywordTable so that the parser can find it.  RTTI might be used
by "WorldContext" to determine what sort of thing got returned from the
parser, but something like:

if ((foo = dynamic_cast<Weapon *>(newEntity))) { ... }
else if ((bar = dynamic_cast<Player *>(newEntity))) { ... }

should probably be avoided in favor of double-dispatch if there are going to
be a lot of cases.

(Currently, I'm using something like this to parse DNS packets: each type
of resource record has its own factory which knows how to interpret the
contents and return a class of the right type.  Any operations are done
using double-dispatch on all the RRs in a Message object.)

Mark Gritter
mgritter at cs.stanford.edu

More information about the MUD-Dev mailing list