C++ shortcomings and workarounds

C++ shortcomings and workarounds

Post by Rainer Deyk » Fri, 20 Mar 1998 04:00:00



Here's my problem:

I'm working on a role-playing game in C++.  The game has several physical
objects
in it, such as NPCs, treasure lying around, signs, etc..  It seems natural
to use C++
classes to classify those objects (i.e. class creature; class npc : public
creature;
class weapon; class armor; etc.).  However, there are several problems with
this
approach.  First, I want to be able to save the game state (being the
combined state
of all physical objects, plus some static data) to a file (in a
platform-independent
manner).  Second, I want to write a game editor which allows me to edit the
fields
in the physical objects.  The problem is that C++ seems to have very little
support
for this.  In particular, I see the following problems:

  1. To save the state of all physical objects, I need to implement a
'save' function
      in every sub-class of physical_object.  This seems redundant, since I
only
      want to write the type of the object and its data members to a file.

  2. To load the state of the physical objects from a file, I need a switch
statement
      (or similar) to create an object once I have read its type from the
file.  I also
      need a 'load' function in each sub-class of physical_object.  This
'load'
      function seems redundant, especially if I already have a 'svae'
function.
      Moreover, having those separate functions seems to introduce a
potential
      source of bugs.

  3. I don't want to make the 'edit' function a member of the object class,
since this
      function is specific to the editor, rather than being shared by
editor and game.
      This means that I need a parallel hierarchy of object_editor objects,
which
      are friends to the physical objects.  It also means that I again have
to duplicate
      the code for exporting/importing the data members of the physical
objects.

I have thought of the following ways around these problem, however, all
have their
own disadvantages:

  1. Write a scripting language to control the physical objects.  I can now
make the
      'save', 'load', and 'edit' functions features of the scripting
language.  This has
      the obvious problem that its a lot of work.  One pleasant side effect
would be
      that I can change object behavior without recompiling.

  2. Use a different language.  This one also seems to be more trouble than
it's
      worth.

  3. Write a wizard which creates the needed functions automatically.  This
has the
      problem that once I entered my data into the wizard, I could not go
back and
      change my mind.

  4. Write a pre-processor to create the necessary functions.  This has the
problem
      that I effectively add proprietary extensions to C++ which may be
incompatible
      which later versions or extensions to C++.

  5. Write a standard interface through which a function exports its
internal structure.
      The problem with that is that it effectively breaks encapsulation,
and that it is
      also a lot of work, although maybe less than the other methods.

Any thoughts, comments, insights?

--
Rainer Deyke

 
 
 

C++ shortcomings and workarounds

Post by Andrew James Johnso » Fri, 20 Mar 1998 04:00:00




[snip]

Quote:>  1. To save the state of all physical objects, I need to implement a
>'save' function
>      in every sub-class of physical_object.  This seems redundant, since I
>only
>      want to write the type of the object and its data members to a file.

Look into Streams.
>  2. To load the state of the physical objects from a file, I need a switch
>statement
>      (or similar) to create an object once I have read its type from the
>file.  I also
>      need a 'load' function in each sub-class of physical_object.  This
>'load'
>      function seems redundant, especially if I already have a 'svae'
>function.
>      Moreover, having those separate functions seems to introduce a
>potential
>      source of bugs.

Look into 'The Factory' Pattern.
Quote:

>  3. I don't want to make the 'edit' function a member of the object class,
>since this
>      function is specific to the editor, rather than being shared by
>editor and game.
>      This means that I need a parallel hierarchy of object_editor objects,
>which
>      are friends to the physical objects.  It also means that I again have
>to duplicate
>      the code for exporting/importing the data members of the physical
>objects.

Copy constructors and afformentioned Streams.
[snip]

dont know if this helps at all ?
--
Andrew James Johnson

 
 
 

C++ shortcomings and workarounds

Post by Jason Shanke » Fri, 20 Mar 1998 04:00:00



> Here's my problem:

<snip>

Quote:>   1. To save the state of all physical objects, I need to implement a
> 'save' function
>       in every sub-class of physical_object.  This seems redundant, since I
> only
>       want to write the type of the object and its data members to a file.

There is no ultra-fantastic solution to this problem, though you don't
need to go so far as overriding 'save' for each subclass.  The problem
is that you have to identify an object type from a known bytecode (your
save game data).  Typically, this is done by enumerating each type and
assigning it a value (ArmorClassID = 1, CreatureClassID =2, etc).  While
this is not generally advisable, I have yet to see a true option to this
approach.

Once you've resigned yourself to this fate, however, the rest is not a
problem.  One possibility is to store your object's data, along with its
id type, in a member struct;

class Creature
{
private:
struct {
        unsigned long classID;
        short health;
        short IQ;
        short whatever;

Quote:} m_data;
};

Then, to allow serialization, you only need to provide accessors in each
subclass:

..
        void *GetObjectData() { return &m_data;}
        int GetObjectDataSize() { return sizeof(m_data)};
..

You can even add a member pointer and size value to the base class to
avoid overriding these functions:

void *GetObjectData { return m_voidData;}
int GetObjectDataSize { return m_dataSize;}

Then all you need to do is assign these values in the constructors of
your subclasses.

Quote:>   2. To load the state of the physical objects from a file, I need a switch
> statement
>       (or similar) to create an object once I have read its type from the
> file.  I also
>       need a 'load' function in each sub-class of physical_object.  This
> 'load'
>       function seems redundant, especially if I already have a 'svae'
> function.
>       Moreover, having those separate functions seems to introduce a
> potential
>       source of bugs.

You can use a prototype or a factory method with a map of type ids to
avoid your switch statement:

map<int,BaseClass *,less<int> > g_myTypeMap;

To create an object of type 'id':

BaseClass *newObject = g_myTypeMap.find(id)->MakeCopy();

Of course, you have to fill this map in, which is about as much code as
a switch statement, so it's only REALLY worth it if you find yourself
making an unknown type in more than one place (which you might).  Also,
you need to define MakeCopy in each subclass:

BaseClass *SubClass::MakeCopy()
{
        return new BaseClass();

Quote:}

To save typing, you can do this with a template or a macro, but the
result is the same.

If you don't want to add a MakeCopy routine to your classes, you can use
a map of templated abstract factories.

Quote:>   3. I don't want to make the 'edit' function a member of the object class,
> since this
>       function is specific to the editor, rather than being shared by
> editor and game.
>       This means that I need a parallel hierarchy of object_editor objects,
> which
>       are friends to the physical objects.  It also means that I again have
> to duplicate
>       the code for exporting/importing the data members of the physical
> objects.

You can probably save yourself some pain here by making the editor aware
of the data formats for the objects and just using the GetObjectData
method above to access the fields.

Otherwise, you could implement a COM-style system where each object
attribute is given a string name and you have Set/Get/Enumerate
functions for reporting field names and values, but this seems a little
much.

Quote:> I have thought of the following ways around these problem, however, all
> have their
> own disadvantages:

>   1. Write a scripting language to control the physical objects.  I can now
> make the
>       'save', 'load', and 'edit' functions features of the scripting
> language.  This has
>       the obvious problem that its a lot of work.  One pleasant side effect
> would be
>       that I can change object behavior without recompiling.

You still need to know what kind of objects to create given a type id
and how to edit object data.  Shifting the problem to a scripting
language doesn't help you much.

Quote:>   2. Use a different language.  This one also seems to be more trouble than
> it's
>       worth.

You also have to consider which other language will really help you.  A
late-binding language like SmallTalk might make your road a little
easier, but you'll have the same troubles in Pascal, C or ASM or that
you have in C++.

Quote:>   3. Write a wizard which creates the needed functions automatically.  This
> has the
>       problem that once I entered my data into the wizard, I could not go
> back and
>       change my mind.

You already have such a "wizard", it's called a text editor.  A wizard
still has to actually generate the code, which means that you need to
decide which form that code will take.

Quote:>   4. Write a pre-processor to create the necessary functions.  This has the
> problem
>       that I effectively add proprietary extensions to C++ which may be
> incompatible
>       which later versions or extensions to C++.

You could probably get away with boilerplate templates and macros
without killing future C++ code.

Quote:>   5. Write a standard interface through which a function exports its
> internal structure.
>       The problem with that is that it effectively breaks encapsulation,
> and that it is
>       also a lot of work, although maybe less than the other methods.

"Breaking encapsulation" isn't always a bad thing.  The best way to
avoid breaking encapsulation is to do what you originally proposed, a
load/save virtual function in every class.

You could "bend" encapsulation by protecting GetObjectData and related
calls and only accessing them from a 'friend' ObjectSerializer:

class BaseClass
{
private:
        friend class ObjectSerializer;
        void *GetObjectData();
        int GetObjectDataSize();
        int GetObjectTypeID();
public:
//blah blah blah

Quote:};

class ObjectSerializer
{
public:
        BaseClass *CreateObject(type);
        void SaveObject(BaseClass *,FileType *);
        void LoadObject(BaseClass *,FileType *);
        //etc.

Quote:};

Philosophically, you could argue about whether you've broken
encapsulation or not.  Practically, you still have all the benefits of
encapsulation.  You can think of ObjectSerializer as part of the
definition of BaseClass, the same way an iterator is part of a
container.

--
Jason Shankel,
Maxis, Inc.

email provider: p o b o x . c o m  
       user id: s h a n k e l
Good luck with that one, spambots.

 
 
 

C++ shortcomings and workarounds

Post by William H. Iv » Sat, 21 Mar 1998 04:00:00




>Here's my problem:

>I'm working on a role-playing game in C++.  The game has several
>physical objects in it, such as NPCs, treasure lying around, signs,
>etc..  It seems natural to use C++ classes to classify those objects
>(i.e. class creature; class npc : public creature; class weapon; class
>armor; etc.).  However, there are several problems with this
>approach.  First, I want to be able to save the game state (being the
>combined state of all physical objects, plus some static data) to a
>file (in a platform-independent manner).  Second, I want to write a
>game editor which allows me to edit the fields in the physical
>objects.  The problem is that C++ seems to have very little support
>for this.  In particular, I see the following problems:

>  1. To save the state of all physical objects, I need to implement a
>'save' function in every sub-class of physical_object.  This seems
> redundant, since I only want to write the type of the object and its
> data members to a file.

If you just want to take a "snapshot" of the internal state of each
object, you could write each object out with something along the
lines of:

void SaveObjectData( void * pObject, int objectSize, int objectCode );
    *
    *
    *
SaveObjectData( &objectInstance, sizeof( OBJECT_TYPE ), OBJ_CODE );

The function would just insert the code, size and data into the save
file - possibly compressing the data on the way. Restoring the data
would just be the reverse. The code could be an index into a table
to make things easier. (Of course, due to padding, byte-order, etc.,
a save game made on one platform might not be compatible with those
on another - but is that really a problem?)

Obviously that's simplified, and can't be used with all the ways you
might declare a class. For example, static members would have to be
handled separately, and pointers would probably not be valid after
reloading since their targets may have moved - to solve that, use
indices into tables, not direct pointers, or provide functions to
reestablish valid pointers after reloading the data. (And you have
to be careful because some things may insert implied pointers.)
Indices might take up less space, too. If you have, say, 150
"personality" functions, the index will fit in a byte, obviously.
-Wm

 
 
 

C++ shortcomings and workarounds

Post by Gyuri Grel » Sat, 21 Mar 1998 04:00:00



Quote:>Here's my problem:
> [sneep]
> The problem is that C++ seems to have very little
>support
>for this.  In particular, I see the following problems:

>  1. To save the state of all physical objects, I need to implement a
>'save' function
>      in every sub-class of physical_object.  This seems redundant, since I
>only
>      want to write the type of the object and its data members to a file.

If there are common characteristics defined in physical_object, you can
actually save those in physical_object, then have the descendants only deal
with saving their specific data:

class physical_object
{
    int xPos, yPos;

    virtual void SaveData(SomeFileClass& file)
    {
        file.WriteInt(xPos);
        file.WriteInt(yPos);
    }

Quote:};

class colored_object : public physical_object
{
    int color;

    virtual void SaveData(SomeFileClass& file)
    {
        physical_object::SaveData(file);
        file.WriteInt(color);
    }

Quote:};
>  2. To load the state of the physical objects from a file, I need a switch
>statement
>      (or similar) to create an object once I have read its type from the
>file.  I also
>      need a 'load' function in each sub-class of physical_object.  This
>'load'
>      function seems redundant, especially if I already have a 'svae'
>function.
>      Moreover, having those separate functions seems to introduce a
>potential
>      source of bugs.

One way to consolidate the save and load, is to implement one function, say
XFerData that takes a boolean which determines weather you are saving or
loading. You'd also need some kind of wrapper class for a file object. It
would work like this:

void physical_object::XFerData(FileClass& file, bool load)
{
    file.XFerInt(xPos, load);
    file.XFerInt(yPos, load);
    file.XFerBlock(ptr, size, load);

Quote:}

void FileClass::XFerInt(int& value, bool load)
{
    if (load)
        ::fread(file, &value, sizeof(int))
    else
        ::fwrite(file, &value, sizeof(int));

Quote:}

This was you only write on function to read or write, and since reading and
writing of pieces happens in the same order, it works ok.

Quote:>  3. I don't want to make the 'edit' function a member of the object class,
>since this
>      function is specific to the editor, rather than being shared by
>editor and game.
>      This means that I need a parallel hierarchy of object_editor objects,
>which
>      are friends to the physical objects.  It also means that I again have
>to duplicate
>      the code for exporting/importing the data members of the physical
>objects.

You could just #ifdef some part of your objects thats specific to the
editor, that way you could exclude that code from the game, but include it
in the editor portion.

But why would the wrapper classes need to duplicate the import/export code,
when it could directly have an instance or a pointer to an instance of its
parallel class, then just call save or load on that.

- Gyuri

PS: I take no responsibility for the code I wrote, its way too early to be
programming ;-)

---------------------------------------------------------------------
to email me, remove nospam from my email address
---------------------------------------------------------------------
Iconoclast Software LLC                                   Gyuri Grell
1739 N. Cliff St.                             gyuri at iconoclast.com
Suite 400                                         Phone: 703.838.7600
Alexandria, VA 22301                                Fax: 703.838.7601

 
 
 

C++ shortcomings and workarounds

Post by TM97 » Sun, 22 Mar 1998 04:00:00




>First, I want to be able to save the game state (being the
>combined state
>of all physical objects, plus some static data) to a file (in a
>platform-independent
>manner).  Second, I want to write a game editor which allows me to edit the
>fields
>in the physical objects.  The problem is that C++ seems to have very little
>support
>for this.

Why not use Smalltalk?
It has all the features you need to solve these two problems:

Methods to store instances as ASCII text and reloading them later on are
already there.
Just use them.
VisualWorks at least has also the possibility to store objects in a binary
storage format,
which is platform independent. Just use another object engine and your image
(i.e. the smalltalk
program) plus your binary storage file will work on that platform as well.

And for editing your physical objects have a look into the object inspectors
and their source code  -
they have all the code to edit any member variable of any Smalltalk object.
Use that knowledge as a basis to create your own game editor.

Or straightaway create your editor as s.th. like this:
Let the user select one of your physical objects,
ask the object for it's class,
ask the class for its attributes (called instance variables),
use their names for creating reading and writing accessor messages,
offer the objects attributes and their values to your user using the names and
reading messages
and write their new values back using their writing messages.

To make this possible you need just a few naming conventions:
name attributes and reading messages with identical names,
name writing messages as <readingMessage=attributeName>:,
i.e. with a colon for the argument.

Is it just the new language or are there other reasons not to try Smalltalk for
your game?

Ted

--
TM

 
 
 

C++ shortcomings and workarounds

Post by Adam » Mon, 23 Mar 1998 04:00:00


I have used the same technique of having derived object classes just
write/read their own specific data, then call its base class.

What you call XFering, is what is known as serializing in MFC.  It works
similarly with a CArchive object.

---
Adam

 
 
 

C++ shortcomings and workarounds

Post by Adam » Mon, 23 Mar 1998 04:00:00


I advise you not to use the technique of having all of the entities save
themselves differently (and appropriately).

I used this technique.  This is okay for savegame files, but for actual game
maps, it sucks, because the file format changes EVERY DAY.  You will be
losing maps you've made every time you make a change in *one* single
entity's file format.

This is basically what would've happened to me in a previous project, if I
had ever gotten as far as writing an editor.  :-)  So, what I have in mind
for my current project is having a file format for entities like this:

Have a number of key-value pairs in the file for each entity... That's it.
Have a number, that says how many pairs there are, then a string for the
key, and a string for the value....

This way, it works like Quake and is expandable, and in your editor, the
user just types in something like "speed" "450" (much like Quake/Quake2).

But then you're going to have to map the file keys to data members of your
entity classes...
So using C++ seems like a pain for this.  But having the game logic like
Quake or even Quake 2's is just a mess in my opinion.  The code is all over
the place in Quake 2's game-logic source code.

So, you might have an array like this:

struct ENTITYFIELDMAP
{
    CHAR* pszName;
    UINT     nFieldOffset;
    enumerated_type FieldType;

Quote:};

ENTITYFIELDMAP aefmBase[NUM_ENTITY_FIELDS] =
{
    { "speed", foffset(CEntity, nSpeed), FIELD_TYPE_INTEGER },
    { "velocity", foffset(CEntity, vecVelocity), FIELD_TYPE_VECTOR },
    { "origin", foffset(CEntity, ptOrigin), FIELD_TYPE_VECTOR }

Quote:};

This way, you can have entity load/save code which reads in the KEY-name in
the map/savegame file, and knows the offset in the CEntity class/structure
of where the value is.  Also, what kind of value it is.

This is basically how Quake 2 does game saving, from what I've seen of the
game DLL source.

But you'll end up needing to have a single entity structure, so that you
don't have to load/save entities differently depending on type.....  Makes
me wish there were a better way.....  Because C++ inheritence works quite
well with entities from my experience.

How about psuedo-C++ through function pointers (which Quake engines do,
actually)?
You can just have an initiailizer function for each entity type....  That
sets the fake vtable.....

(I'm rambling on and on because I'm giving myself ideas, hehe....)

----
Adam

 
 
 

C++ shortcomings and workarounds

Post by Michael Duff » Mon, 23 Mar 1998 04:00:00



> I advise you not to use the technique of having all of the entities save
> themselves differently (and appropriately).

> I used this technique.  This is okay for savegame files, but for actual game
> maps, it sucks, because the file format changes EVERY DAY.  You will be
> losing maps you've made every time you make a change in *one* single
> entity's file format.

Not necessarily.  I save my map and everything by having the objects save and
load themselves into a buffer which is then written to disk.  As you develop the
game, your internal structures change quite a bit.  Unless you do a major
restructuring, you can save an object version value in each object's saved
data.  We don't care about having outdated code being able to read new data, we
just worry about being able to load old data into new code, and then probably
save it out in the new data format when you save.

For example, I have a class that needs to save data A, B, C, and D.  This is
version 1 of the data format.  I save the data like "VersionNumber A B C D".
Now I expand the class and add data items E and F.  This is version 2.  So my
loading code looks something like this

class::LoadData ()
{
read VersionNumber

if (VersionNumber <=1)
  {
  read and store A
  read and store B
  read and store C
  read and store D
  };

if (VersionNumber <= 2)
  {
  read and store E
  read and store F
  };

Quote:};

If of course you dropped some data between versions (say you dropped C in
version 3 of your data), then you would have to separate out the loading code
more completely

if (VersionNumber <= 2)
  {
  // perform loading as in above example
  }
if (VersionNumber  == 3)
  {
  read and store A and B
  read and discard C
  read and store D, E, and F
  };

Each object can have it's own version number, or you can have a global #define
that gives the current version number.  Then your most recent data loop would be

if (VersionNumber <= CURR_VER_NUMBER) ....

You can also define the data version format once per saved file, and then pass
it down to the objects as a parameter to their loading routine.

Using Key and Value pairs as you suggested in your last post is a valid way of
storing data, but it has the disadvantages of taking up a LOT of memory/disk
space, and requiring a lot of extra code and time to parse the saved data.  Key
and Value is excellent for storage bins that will be used by lots of programs
(the Win95 registry), and for files which will be manually edited by users
outside of the game (say with a text editor).

Later,
Michael Duffy

 
 
 

C++ shortcomings and workarounds

Post by Piercarlo Gran » Tue, 24 Mar 1998 04:00:00



>>> 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.

 
 
 

C++ shortcomings and workarounds

Post by Charles Marti » Tue, 24 Mar 1998 04:00:00




> >>> 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.

And I'd thinkk twice about this as well -- inheritance isn't a code
reuse device, it's a modeling technique classifying things together
because of equivalences among their behaviors.  (If it were merely code
reuse, then you couldn't replace a class with another class with the
same behaviors but a different implementaiton.)
 
 
 

C++ shortcomings and workarounds

Post by Michael Duff » Tue, 24 Mar 1998 04:00:00





> > >>> 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.

> And I'd thinkk twice about this as well -- inheritance isn't a code
> reuse device, it's a modeling technique classifying things together
> because of equivalences among their behaviors.  (If it were merely code
> reuse, then you couldn't replace a class with another class with the
> same behaviors but a different implementaiton.)

I'd agree with Rainer that C++ classes are better for structuring code (for
the purpose of reuse among other things) rather than trying to represent
heirarchies of real world objects.  At least this is the case in games where
you have to be careful of any unnecessary overhead.

To the original poster, I too am currently writing an RPG in C++.  However,
for my objects, NPCs, etc, I only have one class, SBObject (SB is for the
company's initials (^_^) ).  An SBObject can represent the main player, an
NPC, a chair, a piece of armor, a book, a trap, a door... pretty much anything
in the game world that isn't a floor or a wall (floors and walls are encoded
into the map array).  Each object has two main things that set it apart from
other objects.  The first are the game scripts associated with the object, and
the run-time compiled games scripts in my engine control AI, reactions to
charaters trying to use or equip it, etc.

The second is a dynamically allocated block of data which contains the stats
for the object.  The stats have three sections, one for all objects (hit
points, weight, name, inventory list, graphic, etc.), one for PCs/NPCs
(strength, dexterity, other graphics, etc.), and one for extra data that is
special to that particular object.  I define the amount of extra data when the
object is created, and this data is usually accessed by the scripts for
permanent storage of variables the scripts use.  When the object is created, I
set flags which tell which of the three stat sections are present.

When I save the objects in the world, I only have to have one save function
for the SBObject.  It calls a special function for each of the three stat
types so that any info that won't be saved by just copying the stat block to
disk can be saved.  This includes linked lists whose heads are stored in the
stats, or any other pointers to data.  When loading objects, I know I only
have to create a single type of object (SBObject), and as I read the data for
that object, I know how much stat memory to allocate, whether any lists need
to be loaded, etc.  For example, I have a character with an inventory.  All
inventory objects are stored in a linked list of SBObject objects.  When
saving the character, I will write out the number of objects in the inventory,
and then call the object save routine for each object in the inventory.

All in all, this method has worked out very well for me.

(Sorry if the above ramblings seem kind of jumbled.  It is late and it has
been a long day... (^_^) )

Later,
Michael Duffy

 
 
 

C++ shortcomings and workarounds

Post by Tim Ottinge » Wed, 25 Mar 1998 04:00:00



> In particular, I see the following problems:

>   1. To save the state of all physical objects, I need to implement a
> 'save' function
>       in every sub-class of physical_object.  This seems redundant, since I
> only
>       want to write the type of the object and its data members to a file.

So, what you want is a non-invasive mapping from objects to storage sothat this
gets easier. Well, sure enough, C++ does not have self-descriptive
variables so this does get (somewhat) harder. But extracting state to save
it is more tedious than troublesome.  Options range from a full set of
accessors
to a memento-style "state object" to a full "description" structure. Still,
the easier part is getting the state out to persist it.

I'm not crazy about inheriting from "persistent", nor about an object
knowing how to save itself to one specific storage mechanism. I can
elaborate if it is warranted.

Quote:>   2. To load the state of the physical objects from a file, I need a switch
> statement
>       (or similar) to create an object once I have read its type from the
> file.  I also
>       need a 'load' function in each sub-class of physical_object.  This
> 'load'
>       function seems redundant, especially if I already have a 'svae'
> function.
>       Moreover, having those separate functions seems to introduce a
> potential
>       source of bugs.

Well, there are things like dispatchers that can clean this upconsiderably. But
rather than a "load" function, have you considered
having an "all values" constructor and having a "builder" or the like?
Remember that not all methods that act on a class should be members
of that class. We have separation of concerns to consider.

Quote:>   3. I don't want to make the 'edit' function a member of the object class,
> since this
>       function is specific to the editor, rather than being shared by
> editor and game.

Exactly.

Quote:>       This means that I need a parallel hierarchy of object_editor objects,
> which
>       are friends to the physical objects.  It also means that I again have
> to duplicate
>       the code for exporting/importing the data members of the physical
> objects.

Right. Looks like you need a general mechanism, not one for each typeof I/O
possible. But remember that the UI editing objects do not have
to be exactly the same objects as the "real" business objects. The stuff
in the UI has to be permitted to have invalid state between field editing.
The real objects should *never* permit that. There is a kind of "unit of
work" when editing objects.

Quote:>   2. Use a different language.  This one also seems to be more trouble than
>      it's worth.

I consider this one pretty often. The self-description problem is muchsmaller
than the referent ownership problem, and these things are much
alleviated by having a language with reflection and a garbage collector.

Have you tried python, for example?

But I want to solve the problems in C++ first. Call it service to mankind,
call it *, but I want to make the generalized mechanism work,
and make it generally available (free).

 
 
 

1. Visual C++ 2.0 bug, know a simple workaround?

HELP!! I am using Visual C++ 2.0 running under WinNT 3.5.  I have
included a small code sample that illustrates the bug. Does anyone know a
workaround for the following problem???

template <class T>
class Crash
{
public:
  typedef Crash<T> self;

  Crash(T i);

// the compiler dies on the next line with the following error message
// E:\...\main.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR
// (compiler file 'msc1.cpp', line 1056)
//Please choose the Technical Support command on the Visual C++
//Help menu, or open the Technical Support help file for more information

  self Bang(T i) { return self(i); }

private:
  T i;

int main ()
{

  Crash<int> c(5);

  return 0;

I encounted this problem when trying to compile HP's STL under Visual
C++.  Any help would be appreciated.  Respond to me directly and I will
post a synopis.

--
------------------------------------------------------------------------------
"And there were more hides and blankets than poles to hang them on."

--
--
------------------------------------------------------------------------------
"And there were more hides and blankets than poles to hang them on."

2. new palm m100

3. visual C++ workaround for default template function

4. Native method from JAVA application

5. ACCESS DDE and Macro shortcomings

6. ADF files on real AMIGA : Possible or not ?

7. Q: Shortcomings In Windows Communications APIs?

8. Brazil - The Tour

9. Any shortcomings for adding NULL to delete?

10. RTTI shortcomings

11. Tell me the shortcoming of UML

12. MFC Doc/View Shortcomings