September 07, 2007
	Hi!

(sorry big code fragments)

I haven't seen any discussion about changes in conts/invariant model recently, so I decided to remember that there are some situations where transitive const is bad.

There is an extract from my code, which I was writting last days (C++ version):

struct channel_state_t { ...; comm_buf_t m_outgoing_buffer; ... };
class ref_channel_state_t {...}; /* smart pointer for channel_state_t. */

struct msg_send_package { ...; channel_id_t m_channel_id; comm_buf_t m_data; ... };

class a_raw_incoming_channel_processor_t
  {
  private:
    typedef std::map< std::string, ref_channel_state_t > channels_map_t;
    channels_map_t m_clients;
    ...
  public :
    void
    evt_send_package( const msg_send_package & cmd )
      {
        std::string client;
        ref_channel_state_t state;
        if( try_find_channel( cmd.m_channel_id, client, state ) )
          {
            // Append outgoing data to outgoing buffer and initiate
            // channel write readyness detection.
            state->m_outgoing_buffer.append( cmd.m_data );
            ...
          }
      }

  private :
    /* This method doesn't change state of object so it is const */
    bool
    try_find_channel(
      const channel_id_t & id,
      std::string & client_name,
      ref_channel_state_t & state ) const
      {
        channels_map_t::const_iterator it = m_clients.find( make_client_name( id ) );
        if( it != m_clients.end() )
          {
            client_name = it->first;
            state = it->second;
          }
        return false;
      }
  ...
};

Class channel_state_t is used as a storage for various channels descriptions. Method try_find_channel doesn't change object but returns non-const reference to channel_state_t because channel_state_t is a subject to modification.

In D 2.0 I can't make try_find_channel const method:

module demo;

import std.string;

class SendPackage
  {
  public :
    uint channel_id;
    byte[] data;
  }

class ChannelState
  {
  public :
    byte[] output_buffer = [];
  };

class ChannelsProcessor
  {
  public :
    void
    send_package(
      const(SendPackage) msg )
      {
        char[] client_name;
        ChannelState state;
        if( try_find_channel( msg.channel_id, client_name, state ) )
          {
            state.output_buffer ~= msg.data;
          }
      }

  private :
    ChannelState[string] channels;

    const bool
    try_find_channel(
      uint id,
      out char[] client_name,
      out ChannelState state )
      {
        client_name = .toString( id ).dup;
        if( client_name in channels )
          {
            state = channels[ client_name ];
            return true;
          }
        return false;
      }
  };

// vim:ts=2:sts=2:sw=2:expandtab

Because:

demo.d(45): Error: cannot implicitly convert expression ((this.channels)[cast(const(char)[])client_name]) of type const ChannelState to demo.ChannelState

Yes I can make try_find_channel non-const. But suppose that there are several auxilary methods those are called in send_package. If those methods are const than compiler (or run-time) could made their invocation in parallel.

That sample is not alone. I can provide more. For example, message-oriented framework. Message subscribes receive messages via const references. It is necessary, because subscribes can work on different threads. Because of that subscribes should not change message objects. But message objects sometimes should contain non-const references to some other objects. Thus:

interface SystemShutdownNotifier
  {
    void
    addSubscriber( MessageReceiver subscriber );
    ...
  }

class MsgShutdownNotifierStarted : Message
  {
  public :
    SystemShutdownNotifier notifier;
  }

class SomeSubscriber : MessageReceiver
  {
  public :
    void
    onShutdownNotifierStarted(
      const(MsgShutdownNotifierStarted) cmd )
      {
         // Oops! I can't do that! :(
         cmd.notifier.addSubscriber( this );
      }
  }

So I propose to allow some exceptions from const transitivity.

-- 
Regards,
Yauheni Akhotnikau