digitalmars.D - Transitive Const in OO programming
- Alex Burton (47/47) Aug 07 2007 I am interested in what the best way to handle situations such as these ...
- Regan Heath (9/77) Aug 08 2007 StateMachine::getResult modifies state and cannot be 'const' either. In...
- Alex Burton (6/15) Aug 08 2007 All it does is gets a value - the fact that internally perhaps several l...
- Regan Heath (8/39) Aug 08 2007 Correct me if I'm wrong but you want to be able to exclude parts of your...
- Regan Heath (44/85) Aug 08 2007 Here you are ;)
- Steven Schveighoffer (4/9) Aug 08 2007 Is there no equivalent for "mutable" in D?
- Alex Burton (2/37) Aug 08 2007 No what I want is to have StateMachineWrapper observe and manipulate Sta...
- Alex Burton (12/17) Aug 08 2007 No what I want is to have StateMachineWrapper observe and manipulate Sta...
- Alex Burton (2/37) Aug 08 2007 No what I want is to have StateMachineWrapper observe and manipulate Sta...
- Sean Kelly (7/12) Aug 08 2007 Because getResult modifies the state of the object? Given this
- Alex Burton (5/18) Aug 08 2007 I don't use c++ mutable either, except sometimes in tests.
- eao197 (90/90) Aug 08 2007 Hi!
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
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
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.ReganThanks 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
Alex Burton wrote: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: "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? ReganStateMachine::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.ReganThanks 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.
Aug 08 2007
Regan Heath wrote:Alex Burton wrote: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(); }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: "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?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.ReganThanks 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.
Aug 08 2007
"Regan Heath" <regan netmail.co.nz> wrote in message news:f9clrl$11ui$1 digitalmars.com...Regan Heath wrote:Is there no equivalent for "mutable" in D? -SteveAlex Burton wrote:Yes it would but then I can't simply call a get method (getResult) using a const reference to StateMachineWrapper.
Aug 08 2007
Regan Heath Wrote:Alex Burton wrote:No what I want is to have StateMachineWrapper observe and manipulate StateMachine without StateMachine being considered part of StateMachineWrapperRegan 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: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.ReganThanks 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.
Aug 08 2007
Regan Heath Wrote: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. AlexCorrect me if I'm wrong but you want to be able to exclude parts of your class from the protection given by 'const', specifically:
Aug 08 2007
Regan Heath Wrote:Alex Burton wrote:No what I want is to have StateMachineWrapper observe and manipulate StateMachine without StateMachine being considered part of StateMachineWrapperRegan 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: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.ReganThanks 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.
Aug 08 2007
Alex Burton wrote:Regan Heath Wrote: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. SeanIt 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.
Aug 08 2007
Sean Kelly Wrote:Alex Burton wrote: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. AlexRegan Heath Wrote: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.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.
Aug 08 2007
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