>>> said:
rainerd> Here's my problem: I'm working on a role-playing game in C++.
rainerd> The game has several physical objects in it, such as NPCs,
rainerd> treasure lying around, signs, etc.. It seems natural to use
rainerd> C++ classes to classify those objects (i.e. class creature;
rainerd> class npc : public creature; class weapon; class armor; etc.).
I would think twice about this; class inheritance relationships arenot a
data modeling device (as obvious when one considers the endless debates
about IS-A, AS-A, ...), but a code reuse one. However in some/many
cases, including probably this one, the data model and the code reuse
shapes are much the same, and one can get away with it.
rainerd> However, there are several problems with this approach. First,
rainerd> I want to be able to save the game state (being the combined
rainerd> state of all physical objects, plus some static data) to a file
rainerd> (in a platform-independent manner). Second, I want to write a
rainerd> game editor which allows me to edit the fields in the physical
rainerd> objects. The problem is that C++ seems to have very little
rainerd> support for this.
It's not a problem -- it is a feature. C++ is a language designed
primarily for system-level programming (even if it is usable for a wide
range of application areas). Here you want to use it for high level
application level programming with persistent storage. It seems a bad
mismatch...
rainerd> In particular, I see the following problems:
rainerd> 1. To save the state of all physical objects, I need to
rainerd> implement a 'save' function in every sub-class of
rainerd> physical_object. This seems redundant, since I only want
rainerd> to write the type of the object and its data members to a
rainerd> file.
This is due to C++ having designed with a goal of minimum overheads;
languages like Objective-C that keep at runtime a description of the
layout of all classes can use a single, generic 'save' method.
rainerd> 2. To load the state of the physical objects from a file, I
rainerd> need a switch statement (or similar) to create an object
rainerd> once I have read its type from the file.
Again, this is entirely outside the intended application of C++: C++ can
be bent to do latent typing, but at the cost of some complication as you
describe. Again, other languages have been designed with other goals.
Here again Objective-C does support this more cleanly.
rainerd> I also need a 'load' function in each sub-class of
rainerd> physical_object. This 'load' function seems redundant,
rainerd> especially if I already have a 'svae' function. Moreover,
rainerd> having those separate functions seems to introduce a
rainerd> potential source of bugs.
Well, if you have a 'save' function that converts from in-memory to
persistent storage format, you will most definitely need a function that
does the opposite, assuming that the two formats are different (as your
requirement that the persistent storage format be platform independent
almost surely implies). In fact, as a rule, the 'save' and the 'load'
functions will look rather different.
rainerd> 3. I don't want to make the 'edit' function a member of the
rainerd> object class, since this function is specific to the
rainerd> editor, rather than being shared by editor and game. This
rainerd> means that I need a parallel hierarchy of object_editor
rainerd> objects, which are friends to the physical objects. It
rainerd> also means that I again have to duplicate the code for
rainerd> exporting/importing the data members of the physical
rainerd> objects.
This is a rather exxagerated solution.
What you need overall, both for saving/restoring and editing, is some
sort of object introspection facility.
C++ was designed for applications, like system level ones, that don't
require introspective facilities, which can impose a cost on programs
that don't use them.
You are just planning to use the wrong language, a bit like use FORTRAN
II for list processing; it can be done, but it requires a lot of effort.
The language nearest to C++ the has much (if not all) of what you need
is Objective-C. There have been dialects of C++ that have additional
introspection features for much similar purposes (SOS C++ springs to
mind, even if I cannot quite remember if it would do all that you want).
rainerd> I have thought of the following ways around these problem,
rainerd> however, all have their own disadvantages:
rainerd> 1. Write a scripting language to control the physical
rainerd> objects. I can now make the 'save', 'load', and 'edit'
rainerd> functions features of the scripting language. This has
rainerd> the obvious problem that its a lot of work. One pleasant
rainerd> side effect would be that I can change object behavior
rainerd> without recompiling.
This is an *excellent* idea. You can design your own game-oriented
language with the required introspection facilities built in. C++ was
designed for tasks like language implementation, and is eminently
suitable. It need not be a lot of work.
In fact it can be very little work: you can use Guile/SIOD/Elk/Tcl/...
as the base language, which you enrich with */introspection
oriented primitives.
rainerd> 2. Use a different language. This one also seems to be more
rainerd> trouble than it's worth.
To me it seems more trouble than it's worth to use a language with a
definite bent towards one type of applications and apply it to another
that is quite different.
Consider the ratyher troublesome alternatives that using C++ as it is
would imly:
rainerd> 3. Write a wizard which creates the needed functions
rainerd> automatically. This has the problem that once I entered
rainerd> my data into the wizard, I could not go back and change my
rainerd> mind.
Ugh.
rainerd> 4. Write a pre-processor to create the necessary functions.
rainerd> This has the problem that I effectively add proprietary
rainerd> extensions to C++ which may be incompatible which later
rainerd> versions or extensions to C++.
Ugh.
rainerd> 5. Write a standard interface through which a function
rainerd> exports its internal structure. The problem with that is
rainerd> that it effectively breaks encapsulation, and that it is
rainerd> also a lot of work, although maybe less than the other
rainerd> methods.
In other words, turn C++ into C++-and-introspection, hacked. Uhm, unless
you really *have* to use C++ it seems rather crude.
rainerd> Any thoughts, comments, insights?
You are using the wrong language... Apart from Objective-C (and
Smalltalk-80, which has been mentioned in another article, and that has
all the required introspective facilities, but may not be appropriate
for * apps), various Scheme/Lisp dialects and implementations
spring to mind as eminently suitable. There are some (commercial!) games
written in Scheme/Lisp (Abuse, if I remember well, is one of them).
Writing a * language, perhaps as a specialized edition of one of
the many excellent extensible/exntesion languages out there, is also
another good idea; I remember that years ago there was an implementation
of ADL/DDL (Adventure/Dungeon Definition Language, from the Berkeley
* Society) on the 4BSD tapes, and it was quite impressively slick.
Knuth in ACP says that a lot of thorny programming problems become much
more tractable if one defines first a suitable app-oriented language...
However all in all I would give Objective-C a good look at; as an
application development language is way, way more productive than C++,
and it can be learned rather easily, and does what you want.