Skip to main content
GameDev.net gamedev.net
🔒 Locked

Odd C++ Error

Started by LordElectro Dec 1, 2001 at 12:01 PM 9 replies 400+ views
Original Post
LordElectro
LordElectro
Well at least I think it''s odd. I have two classes: TDSettings and TDS_POS TDSettings.h looks like this
  
#include "TDS_POS.h"
// other includes


class TDSettings  
{
// lots of snipped stuff

public:

	int GetKeyByPos( TDS_POS& Pos, char *KeyBuffer );
	int GetCatByPos( TDS_POS& Pos, char *CatBuffer );
// lots more snipped stuff

};
  
TDS_POS.h looks like this:
  
#include "TDSettings.h"
//snippage


class TDS_POS  
{
friend class TDSettings;

// snip


public:
	TDS_POS();
	virtual ~TDS_POS();

	void SetType( char NewType, TDSettings& Settings );
	int  SetCat ( const char *Cat, TDSettings& Settings );
	int  SetCat ( TDS_POS& Cat, TDSettings& Settings );
        // note how TDS_POS& works just fine here


	unsigned int NumEntries() { return TableSize; };
};
  
When compiling TDSettings.h, Visual C++ 6 gives me the errors on GetKeyByPos and GetCatByPos, telling me that it did not expect TDS_POS as an identifier. Putting a simple data type like int compiles fine. What am I doing wrong? :/ Exact Errors:
tdsettings.h(111) : error C2061: syntax error : identifier ''TDS_POS''
tdsettings.h(112) : error C2061: syntax error : identifier ''TDS_POS''
 
BetaShare - Run Your Beta Right!
lc_gta
lc_gta
You''re getting this error because you''re trying to use TDS_POS before it has been defined. To me this looks like bad class design. TDSettings depends on TDS_POS, but TDS_POS also depends on TDSettings so no matter which class is defined first you''ll get a similar error to the one you got. If I were you I''d think about either combining the two classes somehow, or going back to the drawing board for these two classes. There may be a way to get this to work that I don''t know, but everytime I''ve done something similar I''ve had to redesign my classes. Hope this helps
jaxson
jaxson
I don''t see anything obviously wrong in what you posted. Are those the only errors that you are getting? Sometimes, a compilation error will cause lots of superfluous messages that will go away when the real culprit is fixed.

If those are really the only meaningful messages you are getting, then I would suggest posting more of the respective files if they aren''t too large. I would also be interested in how you are guarding your headers from multiple inclusion. A simple mistake such as using #ifdef instead of #ifndef could cause a problem like this where two headers are including each other.
LordElectro
LordElectro
lc_gta: Arpgh, you are right. I got confused because MSDN said mutual friendships can be allowed, so I made the silly assumption that classes can be made to rely on each other. Okay, I think I can fix this, TDS_POS doesn't rely that heavily on TDSettings, so just a few modifications and things should be okay. Thanks.

Edited by - LordElectro on December 1, 2001 1:37:25 PM
BetaShare - Run Your Beta Right!
Stoffel
Stoffel
Since you're not using either class in-size in the other, and you're not calling any of the methods of the classes in the .h files, you don't need to #include them in each other's .h files at all. Here's what would work:
(BY THE WAY!!: this board totally mangles #include lines, so I've replaced the # with HASH_
    
// file TDSettings.h

HASH_ifndef __TDSETTINGS_H__
HASH_define __TDSETTINGS_H__
class TDS_POS; // forward declaration

class TDSettings { /* definition */ };
HASH_endif

// file TDSettings.cpp
HASH_include "tdsettings.h" // a .cpp's .h file should ALWAYS be
// the very first #include, except for
// precompiled headers
HASH_include "tds_pos.h" // assuming you use TDS_POS in this .cpp
/* rest of code */

// file Tds_pos.h
HASH_ifndef __TDS_POS_H__
HASH_define __TDS_POS_H__
class TDSettings; // again, a forward declaration
class TDS_POS { /* definition */ };
HASH_endif

// file Tds.cpp
HASH_include "Tds.h"
HASH_include "Tdsettings.h"
/* rest of code */


Again, the reason this will work is because you don't use objects of one class in another--you just use pointers or references and you do not dereference them.

You should always, always, whenever possible, avoid including header files from a header file.

Now, as to whether the above is good design for your problem, I'd say no. You generally don't want two-way dependencies, and from the classes I'd say that TDS_POS is an iterator through TDSettings. What you probably should be doing is making TDS_POS a member class of TDSettings:
        
class TDSettings
{
public: // class definitions also obey permissions..

class POS;
friend POS;
/* .. rest of TDSettings definition .. */
};

class TDSettings::POS // this is in the same file, BTW
{ /* definition of POS */
};


If a client wants a TDS_POS, the way to get at it now is to get TDSettings:os pos. e.g:
TDSettings settings;
TDSettings:os pos;
settings.GetKeyByPos (pos, "buffer");


Edited by - Stoffel on December 1, 2001 3:28:01 PM
LordElectro
LordElectro
Stoffel:

Thanks a lot, this is what I am looking for! However, in the code you provided, how will TDS_POS be able to access data within TDSettings? Will functions like:

int SetCat ( const char *Cat, TDSettings& Settings );

work then? Or is there another way? Because a TDS_POS needs some info out of the TDSettings object it is going to be used on...
BetaShare - Run Your Beta Right!
Oluseyi
Oluseyi
quote:
Original post by LordElectro
...how will TDS_POS be able to access data within TDSettings?

One solution is to have the TDS_POS class maintain a pointer to its associated TDSettings object:

class TDSettings
{
public:
...
class TDS_POS
{
...
private:
friend class TDSettings;
TDSettings *pAssociatedSettings;
};
friend class TDS_POS;
private:
TDS_POS *pPosition;
};

Then, during construction of a TDSettings object, its TDS_POS object would be constructed and have its pAssociatedSettings set to this:
TDSettings::TDSettings(...)
{
...
pPosition = new TDS_POS(...);
pPosition->pAssociatedSettings = this;
...
}
LordElectro
LordElectro
Thanks both.

Here''s what I''m thinking. I''ll have TDS_POS as a member of TDSettings. Then TDSettings will have this function:

TDS_POS *TDSettings::GetPosObject()
{
TDS_POS *ptdsPos;

ptdsPos = new TDS_POS;
ptdsPos->TDSettingsPointer = this;
return ptdsPos;
}

then to get an actual iterator object I do:

TDSettings::TDS_POS *myPos;
TDSettings Settings;

myPos = Settings.GetPosObject();

This way a TDSettings object isn''t limited to one iterator.

Is this acceptable design (design is really important for this class btw)?
BetaShare - Run Your Beta Right!
Oluseyi
Oluseyi
quote:
Original post by LordElectro
This way a TDSettings object isn''t limited to one iterator.

Actually, the preceding solution doesn''t limit you to one iterator either (and does less work). This way, every time you try to obtain an iterator (TDS_POS *), you have to construct a new one. Consider this:

TDS_POS *TDSettings::GetPosObject(void)
{
return pPosition;
}
...
// get two TDSettings iterators; assume tds is a TDSettings object
TDS_POS pOne, pTwo;
pOne = tds.GetPosObject();
pTwo = tds.GetPosObject();

As pointers, each can be independently incremented and decremented, etc. If you think about it, the STL works in similar fashion:
string::iterator iter0, iter1, stop;
iter0 = str.begin();
iter1 = str.begin();
stop = str.end();

I don''t think iter0 and iter1 receive separate pointers.

Furthermore, if each call to GetPosObject() results in a new object being allocated, who is responsible for the deallocation? This way the object retains proper control.
LordElectro
LordElectro
I have looked at that, and looked at it, and I don't see how pOne and pTwo can increment independently. Either I'm not "getting" something, or we have a different idea of what I am trying to do.

Within TDSettings there is a hash table of category structures. Each category structure has (in addition to a few other variables) a hash table for keys within the category. For example you might have something like:

Categories[0].Keys[0]
Categories[0].Keys[1]
Categories[0].Keys[2]
...
Categories[1].Keys[0]
Categories[1].Keys[1]
Categories[1].Keys[2]

etc

And in case you care, Entries is also a custom structure.

Anyway, the size of these tables can and does change as more and more keys or categories are added (note: tables don't change every single time they are increased in size, they go up in size x amount of elements, where x is customizable). Everytime a table is resized, the hash values for all entries have to be recalculated. Categories and Keys within TDSettings are normally accessed through their string names. For example, I can call TDSettings.GetStr( "SomeKeyName", "SomeCatName" ) to retrieve a value for some entry. So what results is a black box inside of which is a blob (the tables) that is constantly shifting around. So far everything works better than I ever expected, and I have come up with a class that has taken me quite a bit of time to write (for something that started out with such a simple goal), but will be used extensively in all my programs for quite some time. Sometimes though, it is advantageous to be able to cycle through the Categories and Keys without actually knowing their string names. This is where TDS_POS comes in. I needed to implement functionality where I can loop through these tables. If the table sizes were static and the positions of the data within them remained constant, a simple int could be used. Unfortunately, the previous two conditions are only guarenteed as long as a Cat/Key isn't added. So what I needed was a way to iterate through the tables, while being sure that I am never attempting to access invalid data elements and that the elements I think are at a certain position are really there.

TDS_POS is capable of storing a position within a category or key table, and "knows" when the value becomes invalid (TDSettings has a private variable called ObjectID that changes everytime the tables change, and TDS_POS stores the current ObjectID when it's specifically assigned to a category or entry table. The two values are checked whenever a TDS_POS is being used to retrieve or set information in the tables.). I need to be able to "point" to multiple tables/positions within same table at once. In most cases where iteration is needed, there are two TDS_POS objects at work, one that is looping through categories, and one that is looping thorugh the keys within that category.

I hope all that made sense, I left a lot of stuff out about how all this works (and why I'm using it), but hopefully you get what I'm getting at. So is your way still work? In which case, how does it work?

And as for deleting TDS_POS objects after, ya, that is a problem with the way I have laid out :/

EDIT: I'm gonna add one more thing to this to clarify everything. The whole reason, as vaguely outlined above, that I cannot use the same TDS_POS is the ObjectID it stores. Each TDS_POS position I'm keeping needs to store an ObjectID to maintain data integrity.

Edited by - LordElectro on December 1, 2001 11:03:59 PM
BetaShare - Run Your Beta Right!
Oluseyi
Oluseyi
quote:
Original post by LordElectro
I have looked at that, and looked at it, and I don''t see how pOne and pTwo can increment independently. Either I''m not "getting" something, or we have a different idea of what I am trying to do.

My bad, you''d need pointers to pPosition to increment independently (and the appropriate concurrence is questionable).

Given the ObjectID requirements, your method is better.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.