
Pages:<< prev 1 next >>

Black Hand

GroupAdministrators
Posts3,707
JoinedJan 1, 2002
struct weapontable { weapontable(); char *flags; /* Default flag set */ char *name; /* Descriptive name */ float weight; /* Base weight */ float cost; /* Base cost/value */ short type; /* Weapon type */ short wd; /* Base damage */ short skill; /* Skill type */ short damage; /* Damage type */ }; vector<weapontable*> w_table; weapontable::weapontable() { init_memory( &flags, &damage, sizeof( damage ) ); } void save_weapontable() { ofstream stream; stream.open( WTYPE_FILE ); if( !stream.is_open() ) { log_string( "Couldn't write to weapontypes file." ); return; } for( int x = 0; x < TWTP_MAX; ++x ) { stream << "#WTYPE" << endl; stream << "Type " << weapon_type[x].type << endl; stream << "Name " << weapon_type[x].name << endl; stream << "WD " << weapon_type[x].wd << endl; stream << "Weight " << weapon_type[x].weight << endl; stream << "Cost " << weapon_type[x].cost << endl; stream << "Skill " << weapon_type[x].skill << endl; stream << "Damage " << weapon_type[x].damage << endl; stream << "Flags " << weapon_type[x].flags << endl; stream << "End" << endl << endl; } stream.close(); } void load_weapontable() { ifstream stream; weapontable *wt; w_table.clear(); stream.open( WTYPE_FILE ); if( !stream.is_open() ) { log_string( "Couldn't read from weapontypes file." ); return; } do { string key, value; char buf[MSL]; stream >> key; strip_lspace( key ); stream.getline( buf, MSL ); value = buf; strip_lspace( value ); if( key == "#WTYPE" ) wt = new weapontable; else if( key == "Type" ) wt->type = atoi( value.c_str() ); else if( key == "Name" ) wt->name = STRALLOC( value.c_str() ); else if( key == "WD" ) wt->wd = atoi( value.c_str() ); else if( key == "Weight" ) wt->weight = atoi( value.c_str() ); else if( key == "Cost" ) wt->cost = atoi( value.c_str() ); else if( key == "Skill" ) wt->skill = atoi( value.c_str() ); else if( key == "Damage" ) wt->damage = atoi( value.c_str() ); else if( key == "Flags" ) { wt->flags = STRALLOC( value.c_str() ); log_printf( ".... %s", wt->flags ); } else if( key == "End" ) w_table.push_back( wt ); } while( !stream.eof() ); stream.close(); } CMDF( do_wtsave ) { save_weapontable(); ch->print( "Done.\r\n" ); } CMDF( do_wtload ) { load_weapontable(); for( int x = 0; x < w_table.size(); ++x ) { weapontable *w = w_table[x]; log_printf( "Type %d, Name %s, WD %d, Weight %d, Cost %d, Skill %d, Damage %d, Flags %s", w->type, w->name, w->wd, w->weight, w->cost, w->skill, w->damage, w->flags ); ch->printf( "Type %d, Name %s, WD %d, Weight %d, Cost %d, Skill %d, Damage %d, Flags %s\r\n", w->type, w->name, w->wd, w->weight, w->cost, w->skill, w->damage, w->flags ); } } Data file: #WTYPE Type 0 Name Not Defined WD 0 Weight 0 Cost 0 Skill 0 Damage 0 Flags brittle End #WTYPE Type 1 Name Dagger WD 4 Weight 4 Cost 200 Skill 2 Damage 6 Flags anticleric antimonk metal End #WTYPE Type 2 Name Claw WD 5 Weight 4 Cost 300 Skill 4 Damage 1 Flags anticleric antimonk metal End #WTYPE Type 3 Name Shortsword WD 6 Weight 6 Cost 500 Skill 1 Damage 6 Flags anticleric antimonk antimage antinecro antidruid metal End #WTYPE Type 4 Name Longsword WD 8 Weight 10 Cost 800 Skill 1 Damage 1 Flags anticleric antimonk antimage antinecro antidruid metal End #WTYPE Type 5 Name Claymore WD 10 Weight 20 Cost 1600 Skill 1 Damage 1 Flags anticleric antimonk antimage antinecro antidruid antibard antirogue twohand metal End #WTYPE Type 6 Name Mace WD 8 Weight 12 Cost 700 Skill 5 Damage 4 Flags antimonk antimage antinecro antirogue metal End #WTYPE Type 7 Name Maul WD 10 Weight 24 Cost 1500 Skill 5 Damage 4 Flags antimonk antimage antinecro antirogue twohand metal End #WTYPE Type 8 Name Staff WD 7 Weight 8 Cost 600 Skill 11 Damage 4 Flags twohand End #WTYPE Type 9 Name Axe WD 9 Weight 14 Cost 800 Skill 9 Damage 3 Flags anticleric antimonk antimage antinecro antidruid antirogue metal End #WTYPE Type 10 Name War Axe WD 11 Weight 26 Cost 1600 Skill 9 Damage 3 Flags anticleric antimonk antimage antinecro antidruid antirogue twohand metal End #WTYPE Type 11 Name Spear WD 7 Weight 10 Cost 600 Skill 10 Damage 7 Flags antimage anticleric antinecro twohand End #WTYPE Type 12 Name Pike WD 9 Weight 15 Cost 900 Skill 10 Damage 7 Flags anticleric antimonk antimage antinecro antidruid antirogue twohand End #WTYPE Type 13 Name Whip WD 5 Weight 2 Cost 150 Skill 3 Damage 5 Flags antimonk organic End
Saavy users might recognize this bit of code, but if not, no worries. Whenever the wtload command is executed, the result is a segmentation fault. The log message displayed last is:
Sun Mar 19, 2006 9:19:46 PM PST :: Samson returns from beyond the void.
Sun Mar 19, 2006 9:19:49 PM PST :: .... brittle
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid antibard antirogue twohand metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... antimonk antimage antinecro antirogue metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... antimonk antimage antinecro antirogue twohand metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... twohand
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid antirogue metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid antirogue twohand metal
Sun Mar 19, 2006 9:19:49 PM PST :: .... antimage anticleric antinecro twohand
Sun Mar 19, 2006 9:19:49 PM PST :: .... anticleric antimonk antimage antinecro antidruid antirogue twohand
Sun Mar 19, 2006 9:19:49 PM PST :: .... antimonk organic
Sun Mar 19, 2006 9:19:49 PM PST :: Type 0, Name Not Defined, WD 0, Weight 0, Cost 0, Skill 0, Damage 0, Flags (null)
That is obviously in the wtload command, suggesting it loaded the data in properly. However the log output says otherwise since the flags value turned NULL. Valgrind bitches about address 0x2 not being allocated, no surprise, it's crashing, I figured that much. The stack is smashed, so a core dump is worthless and Valgrind is of no value once the stack is smashed.
My best guess is that I'm using vector improperly, but I can't see how. Anyone have any ideas?


Black Hand

GroupAdministrators
Posts3,707
JoinedJan 1, 2002
Don't know why I didn't try this before, but I threw the code up on Cygwin and had at it. Since it uses older gcc 3.4.4, I figured it might ferret out a potential compiler problem. Well, that's exactly what this turned out to be. Without altering a single line of code, Cygwin compiled it and ran it without crashing. Which is why I was stumped. Perfectly valid code crashing out for no explainable reason.
I'd file a bug against the compiler for this, but I wouldn't know where to begin. Isolating this for a testcase would be next to impossible. Guess we'll just have to hope that gcc 4.1 fixes this.
I'd file a bug against the compiler for this, but I wouldn't know where to begin. Isolating this for a testcase would be next to impossible. Guess we'll just have to hope that gcc 4.1 fixes this.



Conjurer

GroupMembers
Posts395
JoinedMar 8, 2005
Samson said:struct weapontable { weapontable(); char *flags; /* Default flag set */ char *name; /* Descriptive name */ float weight; /* Base weight */ float cost; /* Base cost/value */ short type; /* Weapon type */ short wd; /* Base damage */ short skill; /* Skill type */ short damage; /* Damage type */ }; vector<weapontable*> w_table; weapontable::weapontable() { init_memory( &flags, &damage, sizeof( damage ) ); }
I'm not a C++ guru, but can you have constructors in structs? At a guess, I'd say unless you change that to be a class, the compiler shouldn't be generating a call to the constructor function, which would thus not be calling init_memory().
cygwin probably doesn't barf on it because windows allocates auto-variables differently and thus instead of causing a seg-fault, it just reads from (or writes to) other memory owned by the process. For that matter, the windows kernel may not even enforce proper memory boundaries if the process is run by a user with administrator privs.


Black Hand

GroupAdministrators
Posts3,707
JoinedJan 1, 2002
Heh. The joy of old posts. At some point I figured out what was wrong with this since the test command loads and outputs what I was expecting it to. It's too bad the rest of my code is in such disarray at the moment or I might be able to remember why I was doing this to begin with.



Sorcerer

GroupMembers
Posts902
JoinedJan 29, 2007
It's perfectly fine to have constructors in structs. A class is nothing more than a struct with visibility modifiers. The problem is that if you initialize an object with malloc, the constructor is not called. You need to initialize it with 'new' if you want the constructor to be called. (Similarly for destroying it with free vs. delete.)


Apprentice

GroupMembers
Posts62
JoinedAug 30, 2005
DavidHaley said:
You need to initialize it with 'new' if you want the constructor to be called.
If you need a pointer you need to initialize it with new. The default constructor (if one exists) will be called as long as you don't allocate a block of memory with malloc. A constructor will also be called, if one exists, if an assignment operation is used and there is no explicit keyword before the constructor.
Examples:
class foo { public: foo(void) { std::cout << "default" << std::endl; } foo(int a) { std::cout << "int" << std::endl; } explicit foo(long b) { std::cout << "long" << std::endl; } }; int main(void) { foo a; // default foo b = 5; // int foo* c = new foo; // default foo* d = (foo*)malloc( sizeof(foo) ); // none foo f = (long)10; // none foo g( (long)10 ); // long return 0; }
Also, I might be remembering wrong but I believe CREATE used calloc and not malloc. The difference being is that calloc does not initialize the memory block. You will need to use memset if you need it initialized. If you require special memory allocation for an object the best method to go about it is to overload the new and delete operators (and not use macros). It may sound ass-backwards, but the way that new and delete were designed they did not replace malloc/calloc and free, but merely used them in the background. Personally, I don't see a reason to ever need to use malloc/free outside of a C application anymore.
This wasn't intended to say that David was wrong, I was merely elaborating. Bored, I guess, ;-)


Black Hand

GroupAdministrators
Posts3,707
JoinedJan 1, 2002
Ooo, double thread necromancy! That's gotta be worth some extra points or something
From "man calloc":
Unless the man page is wrong, calloc initializes the memory. Which means it's a good thing that CREATE wraps calloc. It's malloc that doesn't initialize the block, which I'd suspect is why it isn't used much anymore.
But then, most people are using C++ these days anyway and calloc/malloc aren't in play anymore. At least one would hope they're using new/delete.

From "man calloc":
calloc() allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allo-cated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
malloc() allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
Unless the man page is wrong, calloc initializes the memory. Which means it's a good thing that CREATE wraps calloc. It's malloc that doesn't initialize the block, which I'd suspect is why it isn't used much anymore.
But then, most people are using C++ these days anyway and calloc/malloc aren't in play anymore. At least one would hope they're using new/delete.


Apprentice

GroupMembers
Posts62
JoinedAug 30, 2005
Samson said:
From "man calloc":
calloc() allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allo-cated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
malloc() allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
Yes, that'd be right. I haven't worked with actually allocating memory for a couple of years. My bad :-)
The only time you'll use malloc/calloc/realloc in C++ is when you're overloading the new operator (same thing with free). I have not come across a reason to use those instead of new/delete, and from what I know they merely wrap a call to malloc and then calls the specified constructor. It also throws an std exception (I don't remember the name offhand). I also believe that when new is called it almost always returns an unallocated block of memory off the heap (e.g. there is no realloc in the STL implementation). But I could be wrong :-)
But its important to point out that new has similar functionality to malloc -- it does not initialize to zero -- and therefore you must do that in the constructor (or better - in an initialization list). So, in an instance with a struct with no defined default constructor the block of memory will be uninitialized.


Sorcerer

GroupMembers
Posts902
JoinedJan 29, 2007
Well, actually, when you call 'new', it also calls the constructor of every member object of the object. For instance, if you have a struct that contains string objects, creating one with 'new' will initialize those string objects correctly. That is the default behavior. Note that this does not apply to pointers.
As a minor correction to my remark from a few months ago, the constructor is also called if you create something on the stack, e.g. for:
string s;
string*s = new string();
both call the constructor.
As a minor correction to my remark from a few months ago, the constructor is also called if you create something on the stack, e.g. for:
string s;
string*s = new string();
both call the constructor.



Fledgling

GroupMembers
Posts37
JoinedNov 28, 2007
It'll call constructors for any non-POD type, but it won't do it for basic types. So your ints and so on will still be uninitialised.



Sorcerer

GroupMembers
Posts902
JoinedJan 29, 2007
You're right, I should have made it more clear that when I said 'member object', the word 'object' was very deliberately chosen.
That said, some container types over primitives like int etc. will call the constructor (initializer might be a more appropriate term) when they find that they need to insert objects, e.g. to fill up space in a vector if you inserted past the last element. But that's different from new's behavior.

That said, some container types over primitives like int etc. will call the constructor (initializer might be a more appropriate term) when they find that they need to insert objects, e.g. to fill up space in a vector if you inserted past the last element. But that's different from new's behavior.
Pages:<< prev 1 next >>