Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ SMAUG
➜ SMAUG coding
➜ obj_to_room problem... infinite loops
|
obj_to_room problem... infinite loops
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
Pages: 1 2
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Tue 11 May 2004 12:52 AM (UTC) |
| Message
| I'm having a problem I can't quite pin down. Against many peoples' better judgement I've started down a C++ conversion of the AFKMud codebase. So far that's gone pretty well, even though it's still in the earlier stages. But just today I noticed a pretty serious problem. When doing a hotboot, reloading the object files that were saved prior to the boot creates an infinite loop as it boots up. I believe I've traced it to the function that loads the objects ( logs stop there and go no farther ). I think the obj_to_room conversion is to blame, and as I was examining it, I began to wonder. Just WTF does this thing do anyway? Why does obj_to_room return an OBJ_DATA pointer? It worked perfectly before the conversion, but obviously it's now now. So maybe I'm doing something wrong?
Alternately, maybe I just don't grasp how the obj_to_room code works, so I'm posting what I have here, including the obj_group function as well since that seems to play a key part in what's going on and maybe someone can spot what I've done wrong.
What calls this stuff in the hotboot code:
obj_from_char( tobj );
tobj = obj_to_room( tobj, room, supermob );
The replacement for the above is indicated below:
void read_obj_file( char *dirname, char *filename )
{
room_index *room;
FILE *fp;
char fname[256];
int vnum;
vnum = atoi( filename );
snprintf( fname, 256, "%s%s", dirname, filename );
if( !( room = get_room_index( vnum ) ) )
{
bug( "read_obj_file: ARGH! Missing room index for %d!", vnum );
unlink( fname );
return;
}
if ( ( fp = fopen( fname, "r" ) ) != NULL )
{
sh_int iNest;
bool found;
obj_data *tobj, *tobj_next;
rset_supermob( room );
for ( iNest = 0; iNest < MAX_NEST; iNest++ )
rgObjNest[iNest] = NULL;
found = true;
for(;; )
{
char letter;
char *word;
letter = fread_letter( fp );
if( letter == '*' )
{
fread_to_eol( fp );
continue;
}
if( letter != '#' )
{
bug( "%s", "read_obj_file: # not found." );
break;
}
word = fread_word( fp );
if( !str_cmp( word, "OBJECT" ) ) /* Objects */
fread_obj( supermob, fp, OS_CARRY );
else if( !str_cmp( word, "END" ) ) /* Done */
break;
else
{
bug( "read_obj_file: bad section: %s", word );
break;
}
}
FCLOSE( fp );
unlink( fname );
for( tobj = supermob->first_carrying; tobj; tobj = tobj_next )
{
tobj_next = tobj->next_content;
if( tobj->HAS_FLAG( ITEM_ONMAP ) )
{
supermob->SET_ACT_FLAG( ACT_ONMAP );
supermob->map = tobj->map;
supermob->mx = tobj->mx;
supermob->my = tobj->my;
}
tobj->from_char(); <---------- REPLACEMENT
tobj = tobj->to_room( room, supermob ); <------------ REPLACEMENT
supermob->REMOVE_ACT_FLAG( ACT_ONMAP );
supermob->map = -1;
supermob->mx = -1;
supermob->my = -1;
}
release_supermob();
}
else
log_string( "Cannot open obj file" );
return;
}
| | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #1 on Tue 11 May 2004 12:53 AM (UTC) |
| Message
| 6000 characters... ugh... this is going to get interesting
The old code:
/*
* If possible group obj2 into obj1 -Thoric
* This code, along with clone_object, obj->count, and special support
* for it implemented throughout handler.c and save.c should show improved
* performance on MUDs with players that hoard tons of potions and scrolls
* as this will allow them to be grouped together both in memory, and in
* the player files.
*/
OBJ_DATA *group_object( OBJ_DATA *obj1, OBJ_DATA *obj2 )
{
if ( !obj1 || !obj2 )
return NULL;
if ( obj1 == obj2 )
return obj1;
if( obj1->pIndexData->vnum == OBJ_VNUM_TREASURE || obj2->pIndexData->vnum == OBJ_VNUM_TREASURE )
return obj2;
if ( obj1->pIndexData == obj2->pIndexData
&& QUICKMATCH( obj1->name, obj2->name )
&& QUICKMATCH( obj1->short_descr, obj2->short_descr )
&& QUICKMATCH( obj1->objdesc, obj2->objdesc )
&& ( obj1->action_desc && obj2->action_desc && QUICKMATCH( obj1->action_desc, obj2->action_desc ) )
&& QUICKMATCH( obj1->socket[0], obj2->socket[0] )
&& QUICKMATCH( obj1->socket[1], obj2->socket[1] )
&& QUICKMATCH( obj1->socket[2], obj2->socket[2] )
&& obj1->item_type == obj2->item_type
&& xSAME_BITS( obj1->extra_flags, obj2->extra_flags )
&& obj1->magic_flags == obj2->magic_flags
&& obj1->wear_flags == obj2->wear_flags
&& obj1->wear_loc == obj2->wear_loc
&& obj1->weight == obj2->weight
&& obj1->cost == obj2->cost
&& obj1->level == obj2->level
&& obj1->timer == obj2->timer
&& obj1->value[0] == obj2->value[0]
&& obj1->value[1] == obj2->value[1]
&& obj1->value[2] == obj2->value[2]
&& obj1->value[3] == obj2->value[3]
&& obj1->value[4] == obj2->value[4]
&& obj1->value[5] == obj2->value[5]
&& obj1->value[6] == obj2->value[6]
&& obj1->value[7] == obj2->value[7]
&& obj1->value[8] == obj2->value[8]
&& obj1->value[9] == obj2->value[9]
&& obj1->value[10] == obj2->value[10]
&& !obj1->first_extradesc && !obj2->first_extradesc
&& !obj1->first_affect && !obj2->first_affect
&& !obj1->first_content && !obj2->first_content
&& obj1->count + obj2->count > 0
&& obj1->map == obj2->map
&& obj1->x == obj2->x
&& obj1->y == obj2->y
&& QUICKMATCH( obj1->seller, obj2->seller )
&& QUICKMATCH( obj1->buyer, obj2->buyer )
&& obj1->bid == obj2->bid ) /* prevent count overflow */
{
obj1->count += obj2->count;
obj1->pIndexData->count += obj2->count; /* to be decremented in */
numobjsloaded += obj2->count; /* extract_obj */
extract_obj( obj2 );
return obj1;
}
return obj2;
}
/*
* Move an obj into a room.
*/
OBJ_DATA *obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex, CHAR_DATA *ch )
{
OBJ_DATA *otmp, *oret;
sh_int count = obj->count;
sh_int item_type = obj->item_type;
AFFECT_DATA *paf;
for( paf = obj->first_affect; paf; paf = paf->next )
room_affect( pRoomIndex, paf, TRUE );
for( paf = obj->pIndexData->first_affect; paf; paf = paf->next )
room_affect( pRoomIndex, paf, TRUE );
for( otmp = pRoomIndex->first_content; otmp; otmp = otmp->next_content )
if( ( oret = group_object( otmp, obj ) ) == otmp )
{
if( item_type == ITEM_FIRE )
pRoomIndex->light += count;
return oret;
}
LINK( obj, pRoomIndex->first_content, pRoomIndex->last_content, next_content, prev_content );
obj->in_room = pRoomIndex;
obj->carried_by = NULL;
obj->in_obj = NULL;
obj->room_vnum = pRoomIndex->vnum; /* hotboot tracker */
if( item_type == ITEM_FIRE )
pRoomIndex->light += count;
falling++;
obj_fall( obj, FALSE );
falling--;
/* Hoping that this will cover all instances of objects from character to room - Samson 8-22-99 */
if( ch != NULL )
{
if( IS_ACT_FLAG( ch, ACT_ONMAP ) || IS_PLR_FLAG( ch, PLR_ONMAP ) )
{
SET_OBJ_FLAG( obj, ITEM_ONMAP );
obj->map = ch->map;
obj->x = ch->x;
obj->y = ch->y;
}
else
{
REMOVE_OBJ_FLAG( obj, ITEM_ONMAP );
obj->map = -1;
obj->x = -1;
obj->y = -1;
}
}
if( obj->pIndexData->vnum == OBJ_VNUM_CORPSE_PC && falling < 1 )
write_corpses( NULL, obj->short_descr+14, NULL );
return obj;
}
| | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #2 on Tue 11 May 2004 12:54 AM (UTC) |
| Message
| The new code:
/*
* If possible group obj2 into obj1 -Thoric
* This code, along with clone_object, obj->count, and special support
* for it implemented throughout handler.c and save.c should show improved
* performance on MUDs with players that hoard tons of potions and scrolls
* as this will allow them to be grouped together both in memory, and in
* the player files.
*/
obj_data *obj_data::group( obj_data *obj2 )
{
if ( !this || !obj2 )
return NULL;
if ( this == obj2 )
return this;
if( pIndexData->vnum == OBJ_VNUM_TREASURE || obj2->pIndexData->vnum == OBJ_VNUM_TREASURE )
return obj2;
if( pIndexData == obj2->pIndexData
&& QUICKMATCH( name, obj2->name )
&& QUICKMATCH( short_descr, obj2->short_descr )
&& QUICKMATCH( objdesc, obj2->objdesc )
&& ( action_desc && obj2->action_desc && QUICKMATCH( action_desc, obj2->action_desc ) )
&& QUICKMATCH( socket[0], obj2->socket[0] )
&& QUICKMATCH( socket[1], obj2->socket[1] )
&& QUICKMATCH( socket[2], obj2->socket[2] )
&& item_type == obj2->item_type
&& xSAME_BITS( extra_flags, obj2->extra_flags )
&& magic_flags == obj2->magic_flags
&& wear_flags == obj2->wear_flags
&& wear_loc == obj2->wear_loc
&& weight == obj2->weight
&& cost == obj2->cost
&& level == obj2->level
&& timer == obj2->timer
&& value[0] == obj2->value[0]
&& value[1] == obj2->value[1]
&& value[2] == obj2->value[2]
&& value[3] == obj2->value[3]
&& value[4] == obj2->value[4]
&& value[5] == obj2->value[5]
&& value[6] == obj2->value[6]
&& value[7] == obj2->value[7]
&& value[8] == obj2->value[8]
&& value[9] == obj2->value[9]
&& value[10] == obj2->value[10]
&& !first_extradesc && !obj2->first_extradesc
&& !first_affect && !obj2->first_affect
&& !first_content && !obj2->first_content
&& count + obj2->count > 0
&& map == obj2->map
&& mx == obj2->mx
&& my == obj2->my
&& QUICKMATCH( seller, obj2->seller )
&& QUICKMATCH( buyer, obj2->buyer )
&& bid == obj2->bid ) /* prevent count overflow */
{
count += obj2->count;
pIndexData->count += obj2->count; /* to be decremented in */
numobjsloaded += obj2->count; /* extract_obj */
obj2->extract();
return this;
}
return obj2;
}
/*
* Move an obj into a room.
*/
obj_data *obj_data::to_room( room_index *pRoomIndex, char_data *ch )
{
obj_data *otmp, *oret;
sh_int ocount = count;
sh_int oitem_type = item_type;
AFFECT_DATA *paf;
for( paf = first_affect; paf; paf = paf->next )
pRoomIndex->room_affect( paf, true );
for( paf = pIndexData->first_affect; paf; paf = paf->next )
pRoomIndex->room_affect( paf, true );
for( otmp = pRoomIndex->first_content; otmp; otmp = otmp->next_content )
if( ( oret = otmp->group( this ) ) == otmp )
{
if( oitem_type == ITEM_FIRE )
pRoomIndex->light += ocount;
return oret;
}
LINK( this, pRoomIndex->first_content, pRoomIndex->last_content, next_content, prev_content );
in_room = pRoomIndex;
carried_by = NULL;
in_obj = NULL;
room_vnum = pRoomIndex->vnum; /* hotboot tracker */
if( oitem_type == ITEM_FIRE )
pRoomIndex->light += count;
falling++;
fall( false );
falling--;
/* Hoping that this will cover all instances of objects from character to room - Samson 8-22-99 */
if( ch != NULL )
{
if( ch->IS_ACT_FLAG( ACT_ONMAP ) || ch->IS_PLR_FLAG( PLR_ONMAP ) )
{
SET_FLAG( ITEM_ONMAP );
map = ch->map;
mx = ch->mx;
my = ch->my;
}
else
{
REMOVE_FLAG( ITEM_ONMAP );
map = -1;
mx = -1;
my = -1;
}
}
if( pIndexData->vnum == OBJ_VNUM_CORPSE_PC && falling < 1 )
write_corpses( NULL, short_descr+14, NULL );
return this;
}
| | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #3 on Tue 11 May 2004 12:54 AM (UTC) |
| Message
| The obj_data class I've converted to, in case it helps:
/*
* One object.
*/
class obj_data
{
public:
obj_data() { init_obj_data(); }
~obj_data();
void init_obj_data() { init_memory( &next, &map, sizeof( map ) ); }
/* Internal refs in object.c */
void fall( bool through );
int item_ego();
sh_int get_resistance();
char *oshort();
char *format_to_char( char_data *ch, bool fShort );
void show_list_to_char( char_data *ch, bool fShort, bool fShowNothing );
obj_data *to_char( char_data *ch );
void from_char();
int apply_ac( int iWear );
void from_room();
obj_data *to_room( room_index *pRoomIndex, char_data *ch );
obj_data *to_obj( obj_data *obj_to );
void from_obj();
void extract();
int get_number();
int get_weight();
int get_real_weight();
char *item_type_name();
bool is_trapped();
obj_data *get_trap();
bool extracted();
obj_data *clone();
obj_data *group( obj_data *obj2 );
void split( int num );
void separate();
bool empty( obj_data *destobj, room_index *destroom );
void remove_portal();
char_data *who_carrying();
bool in_magic_container();
void make_scraps();
int hitroll();
char *myobj();
EXTRA_DESCR_DATA *SetOExtra( char *keywords );
bool DelOExtra( char *keywords );
/* External refs in other files */
void armorgen();
void weapongen();
obj_data *next;
obj_data *prev;
obj_data *next_content;
obj_data *prev_content;
obj_data *first_content;
obj_data *last_content;
obj_data *in_obj;
obj_index *pIndexData;
room_index *in_room;
char_data *carried_by;
EXTRA_DESCR_DATA *first_extradesc;
EXTRA_DESCR_DATA *last_extradesc;
AFFECT_DATA *first_affect;
AFFECT_DATA *last_affect;
EXT_BV extra_flags;
struct mob_prog_act_list *mpact; /* mudprogs */
int mpactnum; /* mudprogs */
char *name;
char *short_descr;
char *objdesc;
char *action_desc;
char *owner; /* Who owns this item? Used with personal flag for Sindhae prizes. */
char *seller; /* Who put the item up for auction? */
char *buyer; /* Who made the final bid on the item? */
char *socket[3]; /* Name of rune/gem the item has in each socket - Samson 3-31-02 */
int bid; /* What was the amount of the final bid? */
sh_int day; /* What day of the week was it offered or sold? */
sh_int month; /* What month? */
sh_int year; /* What year? */
sh_int item_type;
unsigned short mpscriptpos;
int magic_flags; /*Need more bitvectors for spells - Scryn*/
int wear_flags;
sh_int wear_loc;
sh_int weight;
int cost;
sh_int level;
sh_int timer;
int value[11]; /* Raised to 11 by Samson on 12-14-02 */
sh_int count; /* support for object grouping */
int rent; /* Oh, and yes, this is being used :) */
int room_vnum; /* Track it's room vnum for hotbooting and such */
sh_int mx; /* Object coordinates on overland maps - Samson 8-21-99 */
sh_int my;
sh_int map; /* Which map is it on? - Samson 8-21-99 */
bool HAS_FLAG( int bit ) { return( xIS_SET( extra_flags, bit ) ); }
void SET_FLAG( int bit ) { xSET_BIT( extra_flags, bit ); }
void REMOVE_FLAG( int bit ) { xREMOVE_BIT( extra_flags, bit ); }
bool HAS_MFLAG( int flag ) { return( IS_SET( magic_flags, flag ) ); }
void SET_MFLAG( int flag ) { SET_BIT( magic_flags, flag ); }
void REMOVE_MFLAG( int flag ) { REMOVE_BIT( magic_flags, flag ); }
bool CAN_WEAR( int bit ) { return( IS_SET( wear_flags, bit ) ); }
void SET_WEAR( int bit ) { SET_BIT( wear_flags, bit ); }
void REMOVE_WEAR( int bit ) { REMOVE_BIT( wear_flags, bit ); }
};
| | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #4 on Tue 11 May 2004 12:56 AM (UTC) |
| Message
| Apologies for the horrific length and repeating posts, but I think having the code visible is necessary in this case. BEen told already that the OBJ_DATA is returned for grouping purposes and I grasp that part, but still can't pin down why this is causing an infinite loop.
Knowing my luck, it's horridly obvious and I'll end up slapping myself silly once it's highlighted. | | Top |
|
| Posted by
| David Haley
USA (3,881 posts) Bio
|
| Date
| Reply #5 on Tue 11 May 2004 01:27 AM (UTC) |
| Message
| Why did people say to not use C++?
One thing you need to look out for is to be extremely careful that every time an OBJ_DATA is created, you use new and *NOT* malloc. If you don't use new, the constructor is not called and so any initialization is lost.
This can cause subtle errors in places. I admit that I haven't looked at all your code but that's something that you really have to watch out for. I ran into the problem over and over again when I converted to C++ as well. If that doesn't fix it I'll look at your code more.
Personally I am a 100% partisan of converting MUD code to C++. Most people who say to stick with C usually say so just because they don't know C++, or because they have horrible misconceptions about it.
I'm actually taking this a few steps further and rewriting SMAUG to be much more object oriented, that is, with proper inheritance, virtual methods, and all that. It's a lot of work, but it's already getting easier to work with. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #6 on Tue 11 May 2004 01:51 AM (UTC) |
| Message
| Well, people on TMC and TMS who seem to discourage the method by which I'm going about this, by taking existing C code and steadily converting things over to C++. But that's beside the point really.
As I was reading your post, this gem showed up on my game log:
Quote:
Log: [*****] BUG: ALARM CLOCK! In section game_loop
Log: Obtained 10 stack frames.
Log: ../src/afkmud(_Z3bugPKcz+0xe1) [0x80ec8af]
Log: ../src/afkmud [0x80e01f9]
Log: /lib/tls/libc.so.6 [0x40166cd8]
Log: ../src/afkmud(_ZN8obj_data10get_weightEv+0x36) [0x8133ba4]
Log: ../src/afkmud(do_put+0x545) [0x809a871]
Log: ../src/afkmud(_Z9interpretP9char_dataPc+0x88a) [0x80e4100]
Log: ../src/afkmud(_Z9game_loopv+0x3c8) [0x80e0858]
Log: ../src/afkmud(main+0x1b5) [0x80e14c9]
Log: /lib/tls/libc.so.6(__libc_start_main+0xf8) [0x40154758]
Log: ../src/afkmud(dlopen+0x41) [0x806c6d5]
Basically it tripped the infinite loop check. Normally it doesn't give me the satisfaction and just hangs the port instead. But this is indicating get_weight is a problem... so...
/*
* Return weight of an object, including weight of contents (unless magic).
*/
int obj_data::get_weight()
{
obj_data *obj;
int oweight;
oweight = count * weight;
/* magic containers */
if( item_type != ITEM_CONTAINER || !HAS_FLAG( ITEM_MAGIC ) )
for( obj = first_content; obj; obj = next_content )
oweight += obj->get_weight();
return oweight;
}
Perhaps this is actually the problem I'm having? Maybe the recursive nature of this function has gone bad on me?
Here's what it used to look like:
/*
* Return weight of an object, including weight of contents (unless magic).
*/
int get_obj_weight( OBJ_DATA *obj )
{
int weight;
weight = obj->count * obj->weight;
/* magic containers */
if ( obj->item_type != ITEM_CONTAINER || !IS_OBJ_FLAG(obj, ITEM_MAGIC) )
for ( obj = obj->first_content; obj; obj = obj->next_content )
weight += get_obj_weight(obj);
return weight;
}
| | Top |
|
| Posted by
| David Haley
USA (3,881 posts) Bio
|
| Date
| Reply #7 on Tue 11 May 2004 02:15 AM (UTC) |
| Message
| I would be very suspicious about the linked list code. Make sure that the constructors are initializing everything properly, and that you don't have any mallocs of classes, and *only* call 'new' to create classes.
Another option is to run some kind of sanity check on the linked lists, make sure that they don't loop onto themselves or something like that. It might be enough to make sure that this->next_content != this, but with the STL it would be fairly trivial to check the whole list for duplicates. Again though, I suspect that if lists are getting corrupted it's because of an initialization that's not happening. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | | Top |
|
| Posted by
| David Haley
USA (3,881 posts) Bio
|
| Date
| Reply #8 on Tue 11 May 2004 02:18 AM (UTC) |
| Message
| Ohhhhhhhhhhhhhhhhh. D'uh!!
for( obj = first_content; obj; obj = next_content )
Note that the for loop is NOT a part of the recursion. I think the best way to explain this is to add 'this' to your code:
for( obj = this->first_content; obj; obj = this->next_content )
In the old C code, 'obj' was just a pointer. In C++ however you have to deal with 'this' changing ... and in fact you have to make it point to the right thing. Your for loop is basically always setting obj right back to the original this's next_content. So you're not even looping over the object's contents, but rather you are looping over the linked list containing object making the call.
I think that it would work to do the following:
for( obj = first_content; obj; obj = obj->next_content )
I'm in a rush so I wasn't very clear with this but I hope you see what I mean... |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #9 on Tue 11 May 2004 02:58 AM (UTC) |
| Message
| Actually I noticed more or less the same problem - but consider this:
The old code brought obj with it from the outside. The new code declared it internally. So wouldn't this:
for ( obj = obj->first_content; obj; obj = obj->next_content )
actually end up literally translated as:
for ( this = this->first_content; this; this = this->next_content )
Now that I think about it, this seems right. But the compiler hates this idea. | | Top |
|
| Posted by
| David Haley
USA (3,881 posts) Bio
|
| Date
| Reply #10 on Tue 11 May 2004 04:13 AM (UTC) Amended on Tue 11 May 2004 04:14 AM (UTC) by David Haley
|
| Message
| for ( this = this->first_content; this; this = this->next_content )
You're not allowed to do that. The 'this' pointer can't be changed, and always refers to the object you are operating on.
The big difference is that the old code didn't have a 'this' pointer, and so could change which object was "active". Your new C++ code always refers to 'this->next_content', because without a specifier it always assumes class variable.
In pseudo-C++, what you want is this:
for ( child = this->first_child; child != NULL; child = child->next_child)
What you actually wrote is:
for ( child = this->first_child; child != NULL; child = this->next_child)
[Edit:]
You're right, though, in theory you would want to change the 'this' pointer. You can't, though, so you have to do it another way. The for loop I gave should work fine. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #11 on Tue 11 May 2004 11:53 AM (UTC) |
| Message
| | Thanks for the assist on that, your modification to the for loop appears to have worked. The mud will now make it through a hotboot. :) | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #12 on Tue 11 May 2004 10:54 PM (UTC) Amended on Wed 12 May 2004 09:36 PM (UTC) by Nick Gammon
|
| Message
| Interesting problem.
I did a conversion of SMAUG to C++, just past the compile stage, and you have obviously got further here, but if I may say, simply converting structs to classes is going to be a bit ugly in the end.
For one thing, as Ksilyan says, you need to use "new" rather than "malloc" if you are going to start using C++ things other than simple variables (ie. other than ints, and pointers) because new calls constructors whereas malloc doesn't. Ditto for "delete" rather than "free".
Next thing, if you are going to use the power of C++ I would start reading about STL (I have a large treatment elsewhere on this forum) and use the STL lists, vectors, queues etc. rather than the old SMAUG way of using defines. For a start, you get type-safety. That is, you don't accidentally link in a player pointer into an object list.
Once you are used to them the STL containers (lists, vectors, queues, stacks etc.) are easy to use and have a very standard interface. For instance, this is a normal way of iterating through *any* container type:
for (some_list_type::iterator i = some_list.begin ();
i != some_list.end ();
i++)
do_something_with (*i);
Iterating from "begin ()" to "end ()" is something that STL supports for almost all containers. Then you simply dereference the iterator to get the underlying item. My example code below shows doing that.
You also have a heap of stuff, like sort, randomise, transform, insert, delete, copy and so on for containers.
Another nice thing is the "string" type, which lets you handle strings without doing mallocs all day long and worrying if the string overflows. eg.
string a = "Nick";
string b = "Gammon";
string c = a + b;
cout << c << endl;
Output
NickGammon
Compare that to C where you have to allocate memory, do strcpy, do strcat, free the memory afterwards, and so on.
Another nice feature is for_each, which I illustrate below. This lets you "walk" a list (or part of a list of you don't use "begin" and "end" but some subset) and apply a function to it. In the example I wrote a "function object" fWeigh, which calculates the weight of an object. A function object is like a function with "state", so that if called repeatedly it can remember something (eg. a cumulative weight).
This is the code for fWeigh:
// function object to get weight of one object
class fWeigh
{
int m_iWeight;
public:
// constructor - zero weight
fWeigh () : m_iWeight (0) { };
void operator() (const MudObject * obj)
{
// add weight of object
m_iWeight += obj->GetWeight ();
// add weight of its contents
m_iWeight += for_each (obj->GetContents ().begin (),
obj->GetContents ().end (),
fWeigh ()).GetTotal ();
};
int GetTotal (void) const { return m_iWeight; }
}; // end of class fWeigh
The constructor initialises the total weight to zero, and the operator () is called each iteration by for_each, and it adds up the weight of the current object. It then recurses to weigh owned objects (objects inside objects).
The full code for this example is in the forum posting:
http://www.gammon.com.au/forum/?bbsubject_id=4181
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #13 on Wed 12 May 2004 12:19 AM (UTC) |
| Message
| Actually I am using 'new' and 'delete' where appropriate. I also do plan to get into the STL stuff as time goes on. But as they say, one thing at a time. Gotta start somewhere, and struct->class seemed like a good place to start.
Everything has gone pretty well so far except for this loop snag, and once I had it in place it was about as obvious as I figured it would be. Almost made me think the original get_obj_weight function might have been buggy from the looks of it.
I'll have a look at your STL stuff when I get more time to keep working on all this. | | Top |
|
| Posted by
| David Haley
USA (3,881 posts) Bio
|
| Date
| Reply #14 on Wed 12 May 2004 12:34 AM (UTC) |
| Message
| The original get_obj_weight wasn't wrong I believe, it was just very bad style. In fact, a lot of SMAUG is pretty bad style, but that's another problem. :-)
Making things STL is not as easy as it sounds. I have found that in the end it's easier to just mostly redesign everything and make it proper C++. Still, the transition from C to C++ is always a good thing to do, if anything because you can start using STL in normal functions. I rewrote the who function to use the STL for example, and it's oh-so-much nicer. :) |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | | Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
45,429 views.
This is page 1, subject is 2 pages long: 1 2
It is now over 60 days since the last post. This thread is closed.
Refresh page
top