 |
Server Help Community forums for Subgame, ASSS, and bots
|
Author |
Message |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Tue Jun 15, 2004 1:30 pm Post maybe stupid Post subject: ostrstream |
 |
|
|
|
(Ekted please ignore this C++ help post.)
I've been trying to write to memory using ostrstream's dynamic buffer feature. However, it seems that whenever I write a bunch of nulls stuff gets totally whacked up. Here's the code that I'm using:
ostrstream tout();
tout.write((char*)&next_uid, 4);
num = 0xCF9328F6;
tout.write((char*)&num, 4);
for (i = 0; i < NUM_PLAYERS; i++)
{
len = strlen(players[i].name);
tout.write(players[i].name, len); //Access violation here on second run-through
NULLS(0x100 - len);
} |
On delving into debug information, it appears that the nulls were written correctly, and the put pointer advanced correctly, but the end pointer unchanged. However, it does seem that the memory was allocated since there is 4 bytes of no-man's-land after the nulls. Then, when the problemating instruction allocates the memory for the 2nd name, the put pointer ends up at some ridiculously small memory address, obviously not in the debug heap.
Help?  _________________ This help is informational only. No representation is made or warranty given as to its content. User assumes all risk of use. Cyan~Fire assumes no responsibility for any loss or delay resulting from such use.
Wise men STILL seek Him. |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Tue Jun 15, 2004 1:53 pm Post maybe stupid Post subject: |
 |
|
|
|
(haha)
players[i].name is not always going to be valid right? You need to test for null. I don't know MERV. Is player[i] always valid? _________________ 4,691 irradiated haggis! |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Tue Jun 15, 2004 8:17 pm Post maybe stupid Post subject: |
 |
|
|
|
This ain't MERV, it's an app that I'm writing for a completely different game.
I've already checked for nulls of course, on entry to ostrstream::write(), the char * parameter has the correct name inside. |
|
Back to top |
|
 |
Maucaub Newbie
Joined: Apr 15 2004 Posts: 12 Offline
|
Posted: Tue Jun 15, 2004 9:23 pm Post maybe stupid Post subject: |
 |
|
|
|
what is NULLS() doing? (not conceptually, I already get that ... what's the actual code) |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Tue Jun 15, 2004 10:29 pm Post maybe stupid Post subject: |
 |
|
|
|
Add this just before the call to tout.write(players[i].name, len):
printf("%08x\n", players[i].name);
See if the value looks "normal" or is very small. Btw, can I see your players struct/class? I hope name is a char * and not a string type. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Tue Jun 15, 2004 11:47 pm Post maybe stupid Post subject: |
 |
|
|
|
Sorry, that was stupid of me not to include my macro.
#define NULLS(size) \
tout.seekp(size, ios::cur); |
That macro works just fine for an ofstream...
I guess whoever wrote this section of the CRT didn't implement seeking well.
class Player
{
public:
Player();
~Player();
void reset();
char name[30];
int stable;
int enable, human, civ;
char ai[_MAX_FNAME];
char *aifile; //mmapping of AI file for exporting
unsigned long resources[4]; //gold, wood, food, stone
int dis_tech[0x1E], dis_unit[0x1E], dis_bldg[0x14];
int ndis_t, ndis_u, ndis_b;
float unknown[6];
float pop;
float camera[2];
short u1, u2;
bool avictory;
char diplomacy[9];
int color;
SVector <Unit> units;
}; |
And I can't exactly printf(), since it's a Windows UI app, but I'll try to implement some message box thingy. That's gonna be annoying seeing as the file my code is in doesn't have windows.h included. Grrr. |
|
Back to top |
|
 |
Mine GO BOOM Hunch Hunch What What

Age:42 Gender: Joined: Aug 01 2002 Posts: 3615 Location: Las Vegas Offline
|
Posted: Wed Jun 16, 2004 12:17 am Post maybe stupid Post subject: |
 |
|
|
|
Mr Ekted wrote: | Add this just before the call to tout.write(players[i].name, len):
printf("%08x\n", players[i].name); |
If you are using a UI, you could always do it this way:
FILE *fDebug = fopen("debug.log", "w+");
if (fDebug)
{
fprintf(fDebug, "%08x\n", players[i].name);
fclose(fDebug);
} |
If you want to this a bunch of times, I'd recommend changing the "w" part to "a+", which means append, instead of creating a new blank file.
Or you could learn to use a debugger and set breakpoints. Those are always handy. What compiler/debugger set you using? If its MSVC 5 or 6, I can help teach you how to play with the debugger. |
|
Back to top |
|
 |
Dustpuppy Server Help Squatter

Age:40 Gender: Joined: Jan 23 2003 Posts: 215 Location: England Offline
|
Posted: Wed Jun 16, 2004 10:27 am Post maybe stupid Post subject: |
 |
|
|
|
I've never used ostrstream before, I was looking around for info on it and it seemed the general consensus was that it was deprecated, and you should use ostringstream instead. _________________
 |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Wed Jun 16, 2004 1:35 pm Post maybe stupid Post subject: |
 |
|
|
|
I've been looking through all the debug functions, followed all the CRT functions and what they're doing. I still can't quite understand what's going on but I'm gonna run through it a few more times and see what's happening. But as I said earlier, my data is not bad, because inside the function, I can see the correct name.
Dust wrote: | I've never used ostrstream before, I was looking around for info on it and it seemed the general consensus was that it was deprecated, and you should use ostringstream instead. |
Yeah, I've seen that before, but sadly ostringstream uses std::strings, which are a part of STL, which I'm not using for reasons Ekted would appreciate  |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Wed Jun 16, 2004 2:00 pm Post maybe stupid Post subject: |
 |
|
|
|
Seems my lasy reply disappeared. I'll try to recreated it.
Please post your players struct/class. I'm guessing the name member is a string type and not a char *, which would be a huge bug. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Wed Jun 16, 2004 2:18 pm Post maybe stupid Post subject: |
 |
|
|
|
OK, after a lot of debugging, I think I found where the error is occuring.
streampos strstreambuf::seekoff(streamoff off, ios::seek_dir dir, int mode)
{
char * tptr;
long offset = EOF; // default return value
//cut out ios::in funcs
if (mode & ios::out)
{
if (!epptr())
{
if (strstreambuf::overflow(EOF)==EOF)
return EOF;
}
switch (dir) {
case ios::beg :
tptr = pbase();
break;
case ios::cur :
tptr = pptr();
break;
case ios::end :
tptr = epptr();
break;
default:
return EOF;
}
tptr += off;
offset = tptr - pbase();
if (tptr < pbase())
return EOF;
if (tptr > epptr())
{
if (x_dynamic)
{
x_bufmin = __max(x_bufmin, (tptr-pbase()));
if (strstreambuf::doallocate()==EOF)
return EOF;
}
else
return EOF;
}
pbump(tptr-pptr());
}
return offset;
} |
OK, as you can see tptr is where the put pointer should be after the seek--the target pointer. It's fine before allocation, but after allocation, the put pointer is now pointing to the end of the already written data in the NEW block of memory, while the target pointer is pointing to where the put pointer should be in the OLD block of memory. Hence, the put pointer is bumped to a section in the old block, and you can imagine where it goes from there.
Oops Microsoft, your runtime library is screwed. |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Wed Jun 16, 2004 2:53 pm Post maybe stupid Post subject: |
 |
|
|
|
I'm skeptical that it's a bug. Don't you think this would have been tested at all? Also, reply to my previous post. |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Wed Jun 16, 2004 3:06 pm Post maybe stupid Post subject: |
 |
|
|
|
Cyan~Fire wrote: | Yeah, I've seen that before, but sadly ostringstream uses std::strings, which are a part of STL, which I'm not using for reasons Ekted would appreciate  |
You still are using some pre-built class to do your work for you. See what happens when you do that? You don't understand what's going on inside, and you get into trouble. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Wed Jun 16, 2004 4:35 pm Post maybe stupid Post subject: |
 |
|
|
|
I already posted my class.
And I do understand (now, at least), what's going on behind the scenes. I'm using an iostream because it's well-documented, and has a pretty good interface, IMO. At some point one does have to resort to pre-written code, you demonstrated that yourself when you suggested using printf() for debugging. |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Wed Jun 16, 2004 5:44 pm Post maybe stupid Post subject: |
 |
|
|
|
You really should learn to interactively debug. The problem you are having would be obvious in about 60 seconds. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Wed Jun 16, 2004 7:03 pm Post maybe stupid Post subject: |
 |
|
|
|
I really dont know what I'm doing wrong... could you perhaps clue me in?
I tried replacing the ostrstream with an ofstream and it worked exactly how it should. The iostreams are all supposed to have the same interface, so I really don't understand how I could be doing anything wrong. |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
Posted: Wed Jun 16, 2004 7:54 pm Post maybe stupid Post subject: |
 |
|
|
|
I don't know how either work. I don't use pre-built classes for anything. It takes only about 10% more work to to it yourself, and there's no overhead. Look at some of the functions sometimes. You call a class method, it calls 7 other class methods deep, and ends up calling malloc(). I just call malloc(). I understand the theory of re-useable code through wrapping functionality, but in practice, I have never found it to be worth the trouble. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Wed Jun 16, 2004 8:32 pm Post maybe stupid Post subject: |
 |
|
|
|
Wait... I thought you know what I was doing wrong... or you were just telling me I could easily find out? I'm confused now.
Anyway, the only reason I'm using wrappers here is for the "dynamic buffer" capability, which doesn't seem to be doing it's job. Believe me, I'd rather use a FILE any day, I love formatted printing.
So if Ekted doesn't know what I'm doing wrong... does anybody else?!  |
|
Back to top |
|
 |
Mr Ekted Movie Geek

Gender: Joined: Feb 09 2004 Posts: 1379 Offline
|
|
Back to top |
|
 |
Maucaub Newbie
Joined: Apr 15 2004 Posts: 12 Offline
|
Posted: Sat Jun 19, 2004 2:40 am Post maybe stupid Post subject: |
 |
|
|
|
I guess this issues has effectively been resolved since my last visit, but I'll add a couple more cents anyway, should it prove valuable to someone.
First, it would seem I share a number of the same opinions and philosophies as Mr.Ekted ... this is NOT how I would have handled this problem. However, if you're truly hard-up to continue along these lines and/or this is useful for future reference, here are a few other pointers.
* As Dustpuppy already mentioned, ostrstream IS deprecated by Standard C++. This implies that implementation is no longer guaranteed and would suggest not using it for new code.
* If you're working with strings, treat them as strings. If working with binary data, treat it as binary data. Your use of write() to write the name is inconsistent with this approach and is sure to cause nothing but headaches one day. The write() function treats the buffer as binary data, hence there is NO null terminator added to the "string". Sure, in this case your intention is to fill the remaining space with 0's, but in general this is asking for trouble. If you truly want to treat it as a string, you can simply use the output operator (<<).
* I would not rely on seekp() for this for two reasons: 1) the reallocation implementation is not as clear when you're not actually writing to the dynamic array, and 2) even if the reallocation occurs properly, the initialization of the memory is unclear. You might get the memory you need, but assuming it will be filled with NULLs is shaky ground. If you want a buffer filled with 0's, put them there yourself. Also, here's an example of where write() would end up causing you trouble. If you don't actually get a buffer properly padded with zeros, your string will not end up null terminated, and something will go wrong. Had you written the string as a string and this happened, the program would never know since the strings are all properly terminated.
* When working with a fixed size record, you'll save yourself these kinds of headaches, as well as improve performance, simply by treating them as fixed sized records. Operate on it as a single unit. When you're writing binary data, it doesn't matter how long the string inside is. Just write the whole buffer. For example, modifying your own code:
char buff[256];
ostrstream tout();
tout.write((char*)&next_uid, 4);
num = 0xCF9328F6;
tout.write((char*)&num, 4);
for (i = 0; i < NUM_PLAYERS; i++)
{
memset( buff, 0, sizeof(buff) );
strncpy( buff, players[i].name, sizeof(buff) );
tout.write( buff, sizeof(buff) );
}
|
This is merely a kludge, but it illustrates the point. In this approach, write() handles the reallocation, a fixed size binary buffer is written each time, and the string contained in the buffer is always padded with zeros up to the buffer size.
* Lastly, if you KNOW how big NUM_PLAYERS is, and you KNOW that you want each player's name to be stored in a 256-byte buffer, there's no need for a dynamically reallocated/resized array. Either create a static packed array (as in Ekted's example), or if the buffer needs to be dynamic, allocate your known size in one fell swoop instead of reallocating and re-free'ing little chunks over and over.
Hope these tips help, if not for this, perhaps for something else. |
|
Back to top |
|
 |
Cyan~Fire I'll count you!

Age:37 Gender: Joined: Jul 14 2003 Posts: 4608 Location: A Dream Offline
|
Posted: Sat Jun 19, 2004 12:37 pm Post maybe stupid Post subject: |
 |
|
|
|
I agree with almost all of your statements, and thanks for your help . However, there are a few instances where your suggestions do not apply to what I'm doing (even though you probably couldn't have known this).
Macaub wrote: | * If you're working with strings, treat them as strings. If working with binary data, treat it as binary data. Your use of write() to write the name is inconsistent with this approach and is sure to cause nothing but headaches one day. The write() function treats the buffer as binary data, hence there is NO null terminator added to the "string". Sure, in this case your intention is to fill the remaining space with 0's, but in general this is asking for trouble. If you truly want to treat it as a string, you can simply use the output operator (<<). |
This would be true if I were just writing a nice, pretty text file, but I'm not. As you can see by my direct writing of num, I'm working with a binary stream here which will be compressed by zlib in the near future. So, I see no reason to mix formatted and unformatted output, when the output really is unformatted. Also, most strings in the file do not rely on null termination, rather a 2-byte length value preceding them.
I definitely do agree with your suggestion to make a buffer for the players' names, and it makes me wonder why I didn't think of this in the first place.
Macaub wrote: | * Lastly, if you KNOW how big NUM_PLAYERS is, and you KNOW that you want each player's name to be stored in a 256-byte buffer, there's no need for a dynamically reallocated/resized array. Either create a static packed array (as in Ekted's example), or if the buffer needs to be dynamic, allocate your known size in one fell swoop instead of reallocating and re-free'ing little chunks over and over. |
I'd love to do it this way, but it really wouldn't end up pretty. The section of code I posted here is just that- a section of code. I'm writing a whole bunch of variable length sections after this, and calculating the size of each before hand would mean a LOT of very messy code and possible errors. I know that the code behind ostrstream is messy anyway, but at least this way it didn't look so on the surface.
However, since I prefer FILE input/output instead of the fstreams, and that I will use Ekted's method for clumping the data beforehand in the future, I will very likely never use iostreams again. The point is moot.  |
|
Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
Software by php BB © php BB Group Server Load: 138 page(s) served in previous 5 minutes.
|