Template Effect without actual Templates

Started by
9 comments, last by neonfaktory 20 years, 10 months ago
Ok, so I finally succumbed to not being able to use templates... http://www.gamedev.net/community/forums/topic.asp?topic_id=155506 Soo, I was hoping to get some opinions on how to implement the ability of my class to handle ''int''s, ''unsigned int''s and ''float''s without havign it templated. What I thought was having 3 constructors for each and then having a variable set inside each according to what type. Then having a billion variables to represent each type and THEN have a private function like "MyVal()" that returns the proper variable when I want to use it. Yes, it is butt-ugly. Yes, I''d be very appreciative if someone could help me think of a different way Thanks for any help
weee!
Advertisement
quote:Original post by neonfaktory
Ok, so I finally succumbed to not being able to use templates...

http://www.gamedev.net/community/forums/topic.asp?topic_id=155506

Soo, I was hoping to get some opinions on how to implement the ability of my class to handle 'int's, 'unsigned int's and 'float's without havign it templated.

The problem is that scrollbars and the like do not actually maintain floating point variables under-the-hood. The underlying type will be an int, while the representation of the value within any associated control will be formatted according to some set of rules. You don't really want a class which can handle different underlying types - you want a class which somehow captures the set of formatting rules that might be applied. For example, a text control might include an int representing the underlying value, with a scaling factor, number of significant digits, a min-max range, etc - whatever applies to your sphere of interest. You can then devise a simple algorithm for formatting values for display.

One question: why are you doing this when you could be using an existing GUI toolkit that already solves the problems you're encountering?

[edited by - SabreMan on June 5, 2003 9:10:25 AM]
Well, for the record, I totally understand HOW a GUI works. The problem is that GUI elements are going to be tied to game values - values that can be floats, ints, unsigned ints, etc. These values could be speed, thrust, health, whatever. At some point, after successive updates, you have to effect the original value which means the GUI has to be able to handle int, floats, etc. I don''t know how else you would do it.

My ''revised'' plan was to have 3 pointers, one to an int, unsigned int, and float. Then after accepting the values and storing the original in the matching pointer, I''ll then do a floating point 0%-100% deal based on the minimum and maximum passed in. Then the float will be casted into whatever the original value (pointed to by one of the 3 pointer types) datatype was. Yeah, if that makes sense
weee!
kylotan (or something like that) answered your quesiton correctly.

class wBase {};template <class T> class wScrollbar:public wBase { T val;};list<wBase*> allObjects;allObjects.Insert(new wScrollbar<int>);


Will work.

So, use templates.

500 error x 1
quote:Original post by neonfaktory
Well, for the record, I totally understand HOW a GUI works. The problem is that GUI elements are going to be tied to game values - values that can be floats, ints, unsigned ints, etc. These values could be speed, thrust, health, whatever.

Right, but that doesn't mean that you have to be able to represent the value as any of those, just that you need an easy conversion to the in-game representation from the GUI rep. The title of your post is a red-herring, as templates don't solve your problem.

There's more than one way to skin a cat...
quote:
At some point, after successive updates, you have to effect the original value which means the GUI has to be able to handle int, floats, etc. I don't know how else you would do it.

Well, you could have a conversion function on the component which initiates an implicit conversion to the target type. Something like this (naive implementation)...
#include <iostream>class C{	class Value	{	public:		Value(int rep, int scale)			: rep_(rep), scale_(scale)		{}		operator double()		{			return double(rep_)/double(scale_);		}		operator int()		{			return rep_;		}	private:		int rep_;		int scale_;	};public:	explicit C(int rep=0, int scale=1)		: rep_(rep), scale_(scale)	{}	Value get_value() const	{		return Value(rep_, scale_);	}private:	int rep_;	int scale_;};int main(){	C c(101, 10);	double d = c.get_value();	assert(d==10.1);	std::cout << "double = " << d << "\n";	int i = c.get_value();	assert(i==101);	std::cout << "int = " << i << "\n";}

Rather than put the implicit conversion in `C' itself, I've put it into a proxy object so that the conversion sequence is only triggered when calling get_value(), else you'd expose yourself to the well-known dangers of implicit conversions.

Does this go any way toward solving your problem?

quote:Original post by C-Junkie
kylotan (or something like that) answered your quesiton correctly.

Not really. Storing objects with different underlying representations in a single container is a trivial task. The real problem is how to perform conversion from GUI representation to in-game representation without having conversions all over the program. The scheme you showed does not solve that problem.

[edited by - SabreMan on June 6, 2003 6:35:36 AM]
@Sabre: I think this is where the problem is. What I meant is I totally understand the method you are talking about, with the scale and all. But in your description, how would you apply that to "myShip->myStats->Thrust;" when that value is say... a float?

Well, whatever you do, GUI(or C) NEEDs to have a pointer to the original object you are editing, because the game does not pause for the GUI. After the GUI changes have been made over many game loops, the local value will be copied back to what you are editing via the pointer. This is where the template comes in. Regardless of how I handle things locally (scale or actual vals) in the end I need to have a pointer to either a float, an int, or an unsigned int. Your example works nice with fixed values and all, but i need a GUI that can accept values of any type to handle. I don''t know if this is any better an explanation

@C-Junkie: Well, I tried that, but got a linker error at the last step (where you actually add the items to the list). Perhaps someone would be willing to check my implementation out? I will admit that polymorphism and virtual stuff is new to me - i probably got some syntax wrong...
weee!
class GUI{public:	// Con/Destructors //	GUI();	~GUI();	virtual void SetBounds(int x1, int y1, int x2, int y2) = 0;	virtual void Draw() = 0;	// Needed by CList //	GUI* GetNext(void)   { return m_Next; }	GUI* GetPrev(void)   { return m_Prev; }	void SetNext(GUI* T) { m_Next = T; }	void SetPrev(GUI* T) { m_Prev = T; }protected:	unsigned int X1, Y1, X2, Y2;	eGUITYPE myTYPE;	// Needed by CList //	GUI* m_Next;	GUI* m_Prev;};// Abstracted Class (w/ polymorphism) so elements can be templatedtemplate <class itemType>class GUIT : public GUI{public:	GUIT(eGUITYPE type, itemType* original, itemType min, itemType max);	~GUIT();	void SetBounds(int x1, int y1, int x2, int y2);	void Draw();private:	itemType*	ORIGINAL;	// Pointer to the Original Value	itemType	local;		// Local Value to be copied back upon acceptance	itemType	MINIMUM;	// Minimum Value Available	itemType	MAXIMUM;	// Maximum Value Available};


That all compiles nicely, whether or not it is wrong, but the line below gives me the Linker Error:
	GUILIST.Add(new GUIT<unsigned int>(GUIchgbar, &UNIVSIZE, 1000, 2000));

UNIVSIZE is a global I made for testing this out and shouldnt run out of scope or anything. Umm, oh yeah, the error is:
error LNK2001: unresolved external symbol "public: __thiscall GUIT::GUIT(enum eGUITYPE,unsigned int *,unsigned int,unsigned int)" (??0?$GUIT@I@@QAE@W4eGUITYPE@@PAIII@Z) 


Thanks to anyone that can help me out here
weee!
you need to implement it in the header file
quote:Original post by neonfaktory
@Sabre: I think this is where the problem is. What I meant is I totally understand the method you are talking about, with the scale and all. But in your description, how would you apply that to "myShip->myStats->Thrust;" when that value is say... a float?

I don't know! What is `myShip->myStats->Thrust'? Why are you chaining object interrelationships like that?

As far as I know, I'm providing an answer to your query:
quote:
`I was hoping to get some opinions on how to implement the ability of my class to handle 'int's, 'unsigned int's and 'float's without havign it templated.


quote:
Well, whatever you do, GUI(or C) NEEDs to have a pointer to the original object you are editing, because the game does not pause for the GUI. After the GUI changes have been made over many game loops, the local value will be copied back to what you are editing via the pointer. This is where the template comes in.

What the heck do templates have to do with having a pointer to the GUI? It sounds to me like you're conflating several different concerns, which is confusing the issue of what you actually want to know. Did you read the code I posted earlier? If you need to go in the other direction (from external rep to GUI rep), you can have an overloaded assignment operator, taking int, double, etc. None of this has anything to do with how you connect up your game with your GUI. For that, you should check out the Model-View-Controller pattern.

Templates are only useful when a decision is to be made at compile-time. If you have this scenario...
class dummy_base{};template<typename T>class component : dummy_base{public:  virtual ~component() = 0 {}private:  T value_;};//...std::list<dummy_base*> component_list;//...component_list.push_back(new component<int>);component_list.push_back(new component<double>);

Then your problem is not solved. For instance, imagine you do this...
dummy_base *some_component = *(component_list.begin());  


You need to discover the concrete type of some_component before knowing what representation you are dealing with. This is a red-herring. You actually need a type which can easily convert to the representation you want - whether that be int, floating-point, or something else.
quote:
Regardless of how I handle things locally (scale or actual vals) in the end I need to have a pointer to either a float, an int, or an unsigned int.

You don't *have to* do that, particularly if you are ensuring implementation concerns are concealed behind suitable interfaces. I've already hinted to you that you can use ratios for implementing what you want, but you're confusing the issue with tangential concerns. What is your real question?
quote:
Your example works nice with fixed values and all

Fixed values?
quote:
but i need a GUI that can accept values of any type to handle. I don't know if this is any better an explanation

No. You do *not* need to handle any type. Your problem domain demands a type which can represent both floating-point values and integral values. A ratio will do that, and I've explained how to convert between a ratio and various external representations.


[edited by - SabreMan on June 6, 2003 11:57:28 AM]
quote:Original post by neonfaktory
error LNK2001: unresolved external symbol "public: __thiscall GUIT::GUIT(enum eGUITYPE,unsigned int *,unsigned int,unsigned int)" (??0?$GUIT@I@@QAE@W4eGUITYPE@@PAIII@Z) 



Your code is correct. There is no problem with what you are trying to do.

Except one thing: you forgot to implement the constuctor of GUIT.

Templated implementations have to be inline with the class declaration. unless you use the ''export'' keyword... and I don''t know if MSVC++ supports that correctly.

This topic is closed to new replies.

Advertisement