www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Transitive Const in OO programming

reply Alex Burton <alexibu mac.com> writes:
I am interested in what the best way to handle situations such as these in real
D code.

Transitive const combined with proper OO (no statics etc) means that no state
can be preserved in const methods, given that everything a class refers to
should be through a member or argument.

Yes I know that I could make StateMachine not a state machine, but in reality
there are state machines, and this one could be a mock one for testing.

It comes down to whether all things that a class may refer to are necessarily
part of that class for the purposes of const.

In D 2.0 a call to a const method will not make any changes to anything that
persists after the call returns unless it changes a mutable argument to that
method. (ignoring statics)
This would require state to be passed in to that method, which would require
state to be passed into any const methods calling it, which breaks
encapsulation.

The code below fails to compile because StateMachineWrapper::getResult calls a
non const member of StateMachine

class StateMachine
{
	int state;
	this()
	{
		state = 0;
	}
	void sendMessage()
	{
		state = 1;
	}
	const int getResult()
	{
		if (state == 1)
		{
			return 10;
			state = 0;
		}
		else
			throw new Exception("not in state to getResult");
	}
};

class StateMachineWrapper
{
	StateMachine mMachine;
	this()
	{
		mMachine = new StateMachine;
	}
	const int getResult()
	{
		mMachine.sendMessage();
		return mMachine.getResult();
	}
};


void main()
{
	const(StateMachineWrapper) wrapper = new StateMachineWrapper;
	int x = wrapper.getResult();	
}
Aug 07 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Alex Burton wrote:
 I am interested in what the best way to handle situations such as
 these in real D code.
 
 Transitive const combined with proper OO (no statics etc) means that
 no state can be preserved in const methods, given that everything a
 class refers to should be through a member or argument.
 
 Yes I know that I could make StateMachine not a state machine, but in
 reality there are state machines, and this one could be a mock one
 for testing.
 
 It comes down to whether all things that a class may refer to are
 necessarily part of that class for the purposes of const.
 
 In D 2.0 a call to a const method will not make any changes to
 anything that persists after the call returns unless it changes a
 mutable argument to that method. (ignoring statics) This would
 require state to be passed in to that method, which would require
 state to be passed into any const methods calling it, which breaks
 encapsulation.
 
 The code below fails to compile because
 StateMachineWrapper::getResult calls a non const member of
 StateMachine
 
 class StateMachine
 {
 	int state;
 	this()
 	{
 		state = 0;
 	}
 	void sendMessage()
 	{
 		state = 1;
 	}
 	const int getResult()
 	{
 		if (state == 1)
 		{
 			return 10;
 			state = 0;
 		}
 		else
 			throw new Exception("not in state to getResult");
 	}
 };
 
 class StateMachineWrapper
 {
 	StateMachine mMachine;
 	this()
 	{
 		mMachine = new StateMachine;
 	}
 	const int getResult()
 	{
 		mMachine.sendMessage();
 		return mMachine.getResult();
 	}
 };
 
 
 void main()
 {
 	const(StateMachineWrapper) wrapper = new StateMachineWrapper;
 	int x = wrapper.getResult();	
 }
StateMachine::getResult modifies state and cannot be 'const' either. In this case I think you need a non-const StateMachine::reset to go back to state = 0; It makes sense, especially if you want to call getResult several times for example. Why call StateMachine::sendMessage inside stateMachineWrapper::getResult? Why does StateMachineWrapper::getResult have to be const? It seems making StateMachineWrapper::getResult non-const solves the problem. Regan
Aug 08 2007
parent reply Alex Burton <alexibu mac.com> writes:
Regan Heath Wrote:

 StateMachine::getResult modifies state and cannot be 'const' either.  In 
 this case I think you need a non-const StateMachine::reset to go back to 
 state = 0;  It makes sense, especially if you want to call getResult 
 several times for example.
 
 Why call StateMachine::sendMessage inside stateMachineWrapper::getResult?
Because it's a state machine and thats how it works.
 Why does StateMachineWrapper::getResult have to be const?
All it does is gets a value - the fact that internally perhaps several layers of code down there is a state machine should not make a nice get method become non const, and in turn prevent it from being used from a const method.
 It seems making StateMachineWrapper::getResult non-const solves the problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
 Regan
Thanks for your reply Regan, but I think you misunderstand my post, this is not a specific programming problem I have, it is some code I have constructed in order to illustrate a conceptual problem. Alex
Aug 08 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Alex Burton wrote:
 Regan Heath Wrote:
 
 StateMachine::getResult modifies state and cannot be 'const'
 either.  In this case I think you need a non-const
 StateMachine::reset to go back to state = 0;  It makes sense,
 especially if you want to call getResult several times for example.
 
 
 Why call StateMachine::sendMessage inside
 stateMachineWrapper::getResult?
Because it's a state machine and thats how it works.
 Why does StateMachineWrapper::getResult have to be const?
All it does is gets a value - the fact that internally perhaps several layers of code down there is a state machine should not make a nice get method become non const, and in turn prevent it from being used from a const method.
 It seems making StateMachineWrapper::getResult non-const solves the
 problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
 Regan
Thanks for your reply Regan, but I think you misunderstand my post, this is not a specific programming problem I have, it is some code I have constructed in order to illustrate a conceptual problem.
Correct me if I'm wrong but you want to be able to exclude parts of your class from the protection given by 'const', specifically: "int state;" in "StateMachine" "StateMachine mMachine;" in "StateMachineWrapper" thus allowing you to label methods which only modify these things as "const". Is that more or less it? Regan
Aug 08 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 Alex Burton wrote:
 Regan Heath Wrote:

 StateMachine::getResult modifies state and cannot be 'const'
 either.  In this case I think you need a non-const
 StateMachine::reset to go back to state = 0;  It makes sense,
 especially if you want to call getResult several times for example.


 Why call StateMachine::sendMessage inside
 stateMachineWrapper::getResult?
Because it's a state machine and thats how it works.
 Why does StateMachineWrapper::getResult have to be const?
All it does is gets a value - the fact that internally perhaps several layers of code down there is a state machine should not make a nice get method become non const, and in turn prevent it from being used from a const method.
 It seems making StateMachineWrapper::getResult non-const solves the
 problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
 Regan
Thanks for your reply Regan, but I think you misunderstand my post, this is not a specific programming problem I have, it is some code I have constructed in order to illustrate a conceptual problem.
Correct me if I'm wrong but you want to be able to exclude parts of your class from the protection given by 'const', specifically: "int state;" in "StateMachine" "StateMachine mMachine;" in "StateMachineWrapper" thus allowing you to label methods which only modify these things as "const". Is that more or less it?
Here you are ;) class StateMachine { int state; this() { state = 0; } void sendMessage() { state = 1; } const int getResult() { if (state == 1) { int* p = cast(int*)&state; *p = 0; return 10; } else throw new Exception("not in state to getResult"); } }; class StateMachineWrapper { StateMachine mMachine; this() { mMachine = new StateMachine; } const int getResult() { StateMachine p = cast(StateMachine)mMachine; p.sendMessage(); return p.getResult(); } }; void main() { const(StateMachineWrapper) wrapper = new StateMachineWrapper; int x = wrapper.getResult(); }
Aug 08 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Regan Heath" <regan netmail.co.nz> wrote in message 
news:f9clrl$11ui$1 digitalmars.com...
 Regan Heath wrote:
 Alex Burton wrote:
 Yes it would but then I can't simply call a get method (getResult)
 using a const reference to StateMachineWrapper.
Is there no equivalent for "mutable" in D? -Steve
Aug 08 2007
prev sibling next sibling parent reply Alex Burton <alexibu mac.com> writes:
Regan Heath Wrote:

 Alex Burton wrote:
 Regan Heath Wrote:
 
 StateMachine::getResult modifies state and cannot be 'const'
 either.  In this case I think you need a non-const
 StateMachine::reset to go back to state = 0;  It makes sense,
 especially if you want to call getResult several times for example.
 
 
 Why call StateMachine::sendMessage inside
 stateMachineWrapper::getResult?
Because it's a state machine and thats how it works.
 Why does StateMachineWrapper::getResult have to be const?
All it does is gets a value - the fact that internally perhaps several layers of code down there is a state machine should not make a nice get method become non const, and in turn prevent it from being used from a const method.
 It seems making StateMachineWrapper::getResult non-const solves the
 problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
 Regan
Thanks for your reply Regan, but I think you misunderstand my post, this is not a specific programming problem I have, it is some code I have constructed in order to illustrate a conceptual problem.
Correct me if I'm wrong but you want to be able to exclude parts of your class from the protection given by 'const', specifically:
No what I want is to have StateMachineWrapper observe and manipulate StateMachine without StateMachine being considered part of StateMachineWrapper
Aug 08 2007
parent Alex Burton <alexibu mac.com> writes:
 Regan Heath Wrote:
 
 Correct me if I'm wrong but you want to be able to exclude parts of your 
 class from the protection given by 'const', specifically:
No what I want is to have StateMachineWrapper observe and manipulate StateMachine without StateMachine being considered part of StateMachineWrapper The StateMachine could be a TCP/IP socket or some other communications channel where in order to get a result you must send a message. There are three concepts embodied in a pointer 'has a reference to' and 'is part of' and 'ownership'. In C++ if you had a reference to an object, the latter two concepts were orthogonal, and could be added or subtracted to best reflect reality. In D as soon as you have a reference to an object the latter two concepts are assumed. I am trying to understand how to make this work in real software. The only solutions I can see at the moment are: To make getResult non const which will leak into any calling classes and will effectively make the whole program impossible to get const correct. To do a const cast somewhere as you have shown - not really ideal - and causes undefined behaviour according to docs. To separate object oriented code with a C style interface wherever this occurs. Essentially getting around const by using static functions and token references - like the c standard library FILE fopen fclose functions. This option is really ugly too. To assume that any real statemachines are always outside the scope of D and done in some low level library written in another language. Not a good policy for a language like D I would think. Alex
Aug 08 2007
prev sibling parent Alex Burton <alexibu mac.com> writes:
Regan Heath Wrote:

 Alex Burton wrote:
 Regan Heath Wrote:
 
 StateMachine::getResult modifies state and cannot be 'const'
 either.  In this case I think you need a non-const
 StateMachine::reset to go back to state = 0;  It makes sense,
 especially if you want to call getResult several times for example.
 
 
 Why call StateMachine::sendMessage inside
 stateMachineWrapper::getResult?
Because it's a state machine and thats how it works.
 Why does StateMachineWrapper::getResult have to be const?
All it does is gets a value - the fact that internally perhaps several layers of code down there is a state machine should not make a nice get method become non const, and in turn prevent it from being used from a const method.
 It seems making StateMachineWrapper::getResult non-const solves the
 problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
 Regan
Thanks for your reply Regan, but I think you misunderstand my post, this is not a specific programming problem I have, it is some code I have constructed in order to illustrate a conceptual problem.
Correct me if I'm wrong but you want to be able to exclude parts of your class from the protection given by 'const', specifically:
No what I want is to have StateMachineWrapper observe and manipulate StateMachine without StateMachine being considered part of StateMachineWrapper
Aug 08 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Alex Burton wrote:
 Regan Heath Wrote:
 
 It seems making StateMachineWrapper::getResult non-const solves the problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
Because getResult modifies the state of the object? Given this particular use case, I think I'd want to be aware of the 'destructive' behavior of getResult. I have found uses for 'mutable' in the past, but mostly for things like mutexes for synchronizing data access rather than the data itself. Sean
Aug 08 2007
parent Alex Burton <alexibu mac.com> writes:
Sean Kelly Wrote:

 Alex Burton wrote:
 Regan Heath Wrote:
 
 It seems making StateMachineWrapper::getResult non-const solves the problem.
Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
Because getResult modifies the state of the object? Given this particular use case, I think I'd want to be aware of the 'destructive' behavior of getResult. I have found uses for 'mutable' in the past, but mostly for things like mutexes for synchronizing data access rather than the data itself.
I don't use c++ mutable either, except sometimes in tests. The key thing here is that I am not 'modifying the state of the object', I am modifying the state of another object I just happen to be maintaining a reference to. There is nothing to say that StateMachine is part of StateMachineWrapper. Alex
Aug 08 2007
prev sibling parent eao197 <eao197 intervale.ru> writes:
	Hi!

I'm sorry, I haven't ability to follow D2.0 discussions last time, so ma=
y  =

be my question had been solved anywhere. But I wonder how to express in =
 =

D2.0 C++ classes like that:

// Collection of client's specific data.
class client_info_t
   {
   public :
     client_info_t()
       : m_socket( 0 )
       , m_queue( 0 )
       {}

     //
     // Getters/Setters.
     //

     ACE_SOCK_Stream *
     socket() const { return m_socket; }

     void
     set_socket( ACE_SOCK_Stream * s ) { m_socket =3D s; }

     ACE_Message_Queue *
     queue() const { return m_queue; }

     void
     set_queue( ACE_Message_Queue * q ) { m_queue =3D q; }

   private :
     ACE_SOCK_Stream * m_socket;
     ACE_Message_Queue * m_queue;
   };

// Clients map.
class client_map_t
   {
   public :
     ...
     // Puts new client info into map.
     void
     insert( const std::string & client_id, const client_info_t & info )=
;

     // Removes client info from map.
     void
     remove( const std::string & client_id );

     // Fetches client info from map.
     // Throws invalid_agrument if client_id is unknown.
     const client_info_t &
     fetch( const std::string & client_id ) const;
     ...
   private :
     std::map< std::string, client_info_t > m_map;
   };

client_map_t::fetch is const method, so it doesn't allow anyone to chang=
e  =

map content. If someone wants to send some data to a client, it can use =
 =

const client_map_t::fetch() and retrive const client_info_t object. Then=
  =

he can use const client_info_t::socket() to send data via non-const  =

ACE_SOCK_Stream pointer. But he can't change content of client_info_t  =

because he has only const reference to client_info_t.


I could suggest that in D2.0 fetch() sould return object of different  =

type. For example:

class ConstrainedClientInfo
   {
   public :
      this( SocketStream s, MessageQueue q ) { m_socket =3D s; m_queue =3D=
 q; }
      SocketStream socket() { return m_socket; }
      MessageQueue queue() { return m_queue; }
   private :
      SocketStream m_socket;
      MessageQueue m_queue;
   }

and

class ClientMap
   {
   public :
     ...
     ConstrainedClientInfo fetch( string client_id );
     ...
   private :
      ClientInfo[string] m_map;
   };

But in such case ClientMap.fetch cannot be const (may be I'm wrong?)  =

because if ClientMap.fetch is const then m_map is also const and  =

ClientInfo reference from m_map is also const, and  =

SocketStream/MessageQueue references from ClientInfo from m_map is also =
 =

const.

-- =

Regards,
Yauheni Akhotnikau
Aug 08 2007