Constant classes

Started by
5 comments, last by Qoy 22 years, 9 months ago
I''m thinking about implementing a system where constants can be read in an organized manner from text files at program startup. The problem with this is that values that could have been stored const or text-substituted in now need to be written once and only once, but accessed many times. I have two ideas in mind: 1) I could just use regular variables and read them in at startup and then just trust myself to not write to them again. 2) I could write a "Constant" template class which contains a value as well as a boolean saying whether or not it''s been written yet. It could have an assignment operator (that assigns it only the first time and throws an assert if attempted to write again) and an operator to convert it to whatever type it holds. The second tactic might be more "correct" in terms of program design, but in your opinion, is it really worth it? It adds 1 byte (or 4 bytes) of extra storage, but shouldn''t degrade performance any further than regular variables would assuming all the operators are forced inline. I just wanted to get people''s opinions on which they think might be more practical.
Advertisement
Use getters instead of making the fields public. Eg:

class Constants
{
private:
int _someConstant;
public:

int someConstant()
{ return _someConstant; }

void loadFromFile(const char*);
};

There''s almost no overhead to getting the values, because the calls will be inlined.

Another option is to only allow the rest of the program to access the instance as a const object. Like if you have a static member to get the object:

static Constants* Constants::getContants();

Turn it into this:

static const Constants* Constants::getConstants();

Whatever pointer the function returns will be converted to a const, and the compiler won''t let any part of the program change fields through this pointer. Of course, there''s nothing to stop someone from casting it to a non-const, but that''s C++ for you.
I wasn't going to make the members public or use getters. Actually, I was thinking of something like this:

template class Constant	{	public:		Constant() { m_value = 0; m_set = false; }		~Constant() {}		T operator=(const T rhs)			{				if(!m_set)				{					m_value = rhs;					m_set = true;					return m_value;				}				else				{					assert(0 && "attempted to set constant twice");					return m_value;				}			}		operator T() { return m_value; }	private:		T m_value;		bool m_set;	};   


This way, nothing at all is exposed to the users of the object that has to do with the implementation of a constant class. Once the object is declared and assigned once:

Constant constFloat;
constFloat = 0.5f;

It won't be modified again without throwing an assert, and it can be used without calling anything (due to the T conversion operator, it can be treated as a T without calling a getter function).

So it's not that the interface will be inefficient: it will act just like a regular constant, except givine a run time error instead of a compile time error if you try to assign it more than once. I was just wondering if people thought it was going overkill to implement something like this rather than just using variables.

By the way, it should be noted that it's not the point that this can't be modified, because like Morbo said, you could just cast the object to an int * or something and modify it that way. The point is just to provide safety from change while programming, assuming the programmer doesn't want to modify the value. (If you want to modify it then there's no point in making it a Constant in the first place, the only point is to catch yourself if you do something you don't really want to).

Edited by - Qoy on June 23, 2001 10:06:51 PM
int cookie_init;const int & cookie = cookie_init;void Init(){    cookie_init = 3;}extern const int & cookie;void Use(){    int x = cookie;    cookie = 6;      // error} 

Put the Init part into a seperate module for double safety (so people can''t see cookie_init)

-Mike
-Mike
Put the checking code inside a
#ifdef _DEBUG
#else //RELEASE
#endif

that way you can be certain no one's overwriting it in a debug build, and consequentially know it's constant in a release build - AND avoid the overhead.

Of course, ram stompage is always bad.



Or you could do this:

  class CConstTest	{	public:		CConstTest(int i) : m_Const(i)			{			}		const int m_Const;	};	/...	CConstTest ConstTest(13);	CConstTest ConstTest2(130);	//Prints 13 130	cout<<ConstTest.m_Const<<" "<<ConstTest2.m_Const<<endl;	//error C2166: l-value specifies const object	ConstTest.m_Const = 12; //compile error  


I knew there was a way to set constants at runtime, I had forgotten.

Edited by - Magmai Kai Holmlor on June 24, 2001 2:01:02 AM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Thanks Magmai. The only problem with the method in the bottom of your post is that you must declare and set the constant at the same time. This rules out having a global (or module/class static) constant and setting it once based on text file input, because it''s already declared with no constructor param (or a default).

I don''t think it''s necessary to wrap the checking code in #ifdef _DEBUG blocks because the only thing that involves checking is the assignment operator, and the assignments are supposedly going to be made in initialization functions, not in real time, so an extra if or two have no real consequences.

Well, judging from the response, I guess the idea is not overkill.

Thanks!
You could make a static member function that:

a) loads the values into temporary variables
b) instantiates (and returns) a constant instance of whatever you need, passing the temporary as a parameter to initialise the constant.

That way, the constant is only created once you load the stuff in, meaning you can make it constant at compile time instead of using some run-time mechanism.

This topic is closed to new replies.

Advertisement