Read only public members of class.

Started by
25 comments, last by sirGustav 16 years, 3 months ago
I think gnu c++ may be lacking that functionality. I'm sure there is a way around it, however I'm going with Lode's idea of having the variables stored in a struct and having a single const getter to access the struct. It ticks all the boxes; I can have access to all the (many) variables required for the explosion, it allows the explosion class to modify its own behaviour, and it prevents other classes from corrupting its properties.
I just wanted to see if he would actually do it. Also, this test will rule out any problems with system services.
Advertisement
Quote:
Original post by shotgunnutter
Thanks! I was just halfway through researching that on cplusplus.com, and tearing my hair out while I was at it. Except, it doesn't compile on gcc:

"/Users/shotgunnutter/Projects/ode_collision/current_copy/readonly.h:14: error: a class-key must be used when declaring a friend
/Users/shotgunnutter/Projects/ode_collision/current_copy/readonly.h:14: error: friend declaration does not name a class or function"


Try it with this addition:

template <typename T, typename FriendType>class ReadOnly{public:	class FriendType;    // <-- Added        friend FriendType;	operator const T() const // adds elegance though also a source for errors!	{		return _value;	}		const T get() const // or const T operator()() const	{		return _value;	}private:	ReadOnly() : _value() {}	ReadOnly(const ReadOnly & copy_) : _value(copy_.get()) {}	ReadOnly(const T & assign_)	: _value(assign_) {}	ReadOnly& operator =(T assignCopy_);        //....				T _value;};//So a read-only member would look like this:class SomeClass{	public:        ReadOnly<int, SomeClass> readOnlyInt;};
Quote:
Original post by shotgunnutter
Quote:Original post by Enigma
I would prefer to implement such classes by (for example) moving explosion's behaviour into the explosion class such that clients do not need to get and set variables but rather ask explosion instances to do things:
*** Source Snippet Removed ***

I considered that as well. But, since the explosion class is visible to the renderer, the AI, and the physics engine (among others) that would result in a lot of functionality embedded in the explosion class.

The alternative, of course, is to have more than one kind of explosion, one for each system that recognises an "explosion" as an event, object to avoid, something to draw, etc.


Just out of curiosity - why don't you want to use this approach?
Just out of curiosity - why don't you want to use this approach?
Consider the following:

player.setPosition(player.getPosition() + toMove);
and
player.move(toMove);

When you want to change something (say add collision checks) you are forced to do it wherever you have the set/get. code abstraction at its finest. In a perfect world the get() functions are meant for acquire the current state of the object, and set() for resetting the state(not modifying it)
Besides, I think it is more readable :)

Back on the topic...
This is a rather nice class that I whipped up just recently, it doesnt handle most operators(++, +=, +) , but then again those are not (really) needed(a += b == a =a+b). Someone might want to add those (and are free to do so).

#pragma warning(push)#pragma warning(disable:4100)// aType is unreferenced, let's ignore warningtemplate<typename Type>class ReturnTrue {public:	bool operator()(const Type& aType) {		return true;	}};#pragma warning(pop)template<typename Type, Type aConstValue>class Above {public:	bool operator()(const Type& aType) {		return aType > aConstValue;	}};template<typename Type, class Validator=ReturnTrue<Type> >class Attribute {public:	Attribute() : mIsLocked(false) {	}	explicit Attribute(const Type& aNewValue) : mIsLocked(false), mValue(aNewValue) {		AssertValid(aNewValue);	}	const Attribute& operator=(const Type& aNewValue) {		AssertValid(aNewValue);		AssertLocked();		mValue = aNewValue;		return *this;	}	operator const Type&() const {		AssertValid(mValue);		return mValue;	}	// constify this, prevent changes	void lock() {		mIsLocked = true;	}	void stream(std::istream& aStream) {		AssertLocked();		aStream >> mValue;		AssertValid(mValue);	}	void stream(std::ostream& aStream) const {		AssertValid(mValue);		aStream << mValue;	}private:	static void AssertValid(const Type& aValue) {		assert( Validator()(aValue) );	}	void AssertLocked() {		assert(!mIsLocked);	}	Type mValue;	bool mIsLocked;};template<typename Type, class Validator>std::ostream& operator<<(std::ostream& aStream, const Attribute<Type, Validator>& aAttribute) {	aAttribute.stream(aStream);	return aStream;}template<typename Type, class Validator>std::istream& operator>>(std::istream& aStream, Attribute<Type, Validator>& aAttribute) {	aAttribute.stream(aStream);	return aStream;}


Test case:
class Foo {public:	Foo() : pInt(3), pFloat(0) {}	Attribute<int, Above<int, 0> > pInt;	Attribute<float> pFloat;	Attribute<std::string> pString;};void main() {	Foo bar;	bar.pString = "Hello world";	bar.pFloat = bar.pInt + 39.0f;	// bar.pInt = -5;	// assert() since pInt is going to be lower than 0		std::cout << bar.pString << std::endl		<< "The meaning of life is " << bar.pFloat << std::endl;	std::cin.get();}
Quote:
Original post by sirGustav
Quote:Just out of curiosity - why don't you want to use this approach?

Consider the following:

player.setPosition(player.getPosition() + toMove);
and
player.move(toMove);

When you want to change something (say add collision checks) you are forced to do it wherever you have the set/get. code abstraction at its finest. In a perfect world the get() functions are meant for acquire the current state of the object, and set() for resetting the state(not modifying it)
Besides, I think it is more readable :)


It sounds like you also prefer to use the approach Enigma suggested, so I don't really understand what you're trying to say...
To fix this:
#pragma warning(disable:4100)// aType is unreferenced, let's ignore warningtemplate<typename Type>class ReturnTrue {public:	bool operator()(const Type& aType) {		return true;	}};


do the following:
template<typename Type>class ReturnTrue {public:	bool operator()(const Type & ) {		return true;	}};
d'oh, never though of that :)

This topic is closed to new replies.

Advertisement