Search code examples
class-designdmutable

How to rewrite C++ code that uses mutable in D?


If you needed to rewrite the following C++ code in D, how would you do it?

struct A{

    const S* _s;
    B _b;
    C _c;
    mutable C _c1, _c2;

    A(const B& b, const C& c, const S* s){ /*...*/ }

    void compute(const R& r) const
    {
      //...
      _c1 = ...
      _c2 = ...
    }
};

D doesn't have mutable, and, based on my experience, it's rarely used in C++. But, assuming mutable is used for the right reasons here, what are my options in D?


Solution

  • You have three options to deal with this:

    1. Cast away the const. This will shit the compiler up, but there's no guarantee that your code will work as intended. In particular, if you call that function on the same object from multiple threads then you are at the mercy of data races.

    2. Use an external data structure to store the mutable objects:

      struct A
      {
          static C[const(A)*] _c1s, _c2s;
      
          void compute(ref const(R) r) const
          {
              _c1s[&this] = ...
              _c2s[&this] = ...
          }
      }
      

      I'm using &this as the key to the external hash table, but you would probably be better off using some sort of unique ID. This is a very ugly and tricky solution. I dislike it. Also, note that the hash tables are thread-local, so the same object will have different values on different threads. This may or may not be desirable for your particular application.

    3. Rethink how you use const in D.

      In D, const is transitive and bitwise i.e. logical const is not supported. The purpose of this is to guarantee against concurrent shared data writes. Even though your code may be logically const correct, it will still break if two threads tried to call compute on the same object, so D disallows it and provides no legal escape (no mutable).

      Essentially, you should mark functions as const only when they are bitwise const.

      The result of this is that you should use const a lot less in D than you would in C++ because you require bitwise const a lot less than you require logical const.

      As an example, consider a simple (pointless) generic equal function that tells you if two objects are equal:

      bool equal(T)(T lhs, T rhs) { return lhs == rhs; }
      

      Notice that I haven't marked the function parameters as const. This is on purpose. Testing for equality should not require bitwise const -- it only requires logical const, so enforcing D's level of const on the objects would be needlessly restrictive.

      As jA_cOp says, the D community sees no room for logical const in D, for better or for worse. The problem arises when you try to use D's const like C++'s const. They are not the same, so don't use them in the same way! If there's any possibility at all that a function may require the use of logical const then do not mark them as bitwise const!