enforcing mutability

1. suppose I have a class called Cement whose instances are immutable when just read from the database or are just written to the database

2. all other instantiations in memory (generated by other classes) are mutable until written to the database

what do you think is the most expressive approach to enforce these constraints in C++ code with minimal comments and explanations?

sidenote: this kind of pattern is useful for real-life problems like DB time-travel (anything written to the DB becomes immutable and undeletable), or calculating Money values and recording them to the DB in accounting systems (any reported Money values become real, in contrast to calculated values which may have fractional pennies)
closed account (3hM2Nwbp)
One thing that instantly jumped into my head ( for better or for worse ;P ) would be a reference tracking system like boost::weak_ptr has implemented. Essentially, the idea would be to wrap the Cement classes with a template class, and when it comes time to mutate that class, you would have to call the equivalent of boost::weak_ptr::lock() on it in order to effectively "lock in" a mutation. Boost::weak_ptr returns a null pointer if no references are still made to the shared object. Your implementation might return a null pointer if a state flag on the shared object is set.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <boost/smart_ptr.hpp>

template<typename T>
void mutate(boost::weak_ptr<T> weakPtr)
{
    boost::shared_ptr<T> sharedPtr = weakPtr.lock();
    if(sharedPtr != nullptr)
    {
        //do something
    }
    else
    {
        
    }
}

class Thingy { };

int main()
{
    boost::weak_ptr<Thingy> weakPtr;
    {
        boost::shared_ptr<Thingy> thingyPtr(new int(4));
        weakPtr = boost::weak_ptr<Thingy>(thingyPtr);
        mutate(weakPtr); // something is done to the object pointed to
    }
    mutate(weakPtr); // Nothing is done to the object pointed to.
}


I can't compile this where I'm located right now, but you get the idea. If not, let me know.

Carefully watch the following code, it hasn't been compiled. Obviously left out reference tracking, debug assertions, synchronization issues, exception safety, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
template<typename T>
class MutableReference
{
    private:
        T* object_;
    public:

        MutableReference(T* object) : object_(object) { }

        MutableReference(void) : object_(0) { }

        T* operator*(void)
        {
            return this->object_;
        }

        ~MutableReference(void) { }
};

template<typename T>
class ImmutableReference
{
    private:
        
        T* object_;
    public:

        ImmutableReference(T* object) : object_(object) { }

        ImmutableReference(void) : object_(0) { }

        const T* operator*(void) const
        {
            return this->object_;
        }

        MutableReference<T> lockMutation(void) const
        {
            MutableReference<T> rv;
            if(object_->canMutate())
            {
                rv= MutableReference<T>(object_);
            }
            return rv;
        }

        ~ImmutableReference(void) { }
};

class Cement
{
    public:
        bool canMutate(void) const
        {
            return true;
        }
};

int main()
{
    ImmutableReference<Cement> ref(new Cement());
    Cement* cement= *ref; // <-- Error, cannot convert const Cement* to Cement*
    const Cement* cement2 = *ref; // <-- OK
    MutableReference<Cement> mutableRef = ref.lockMutation();
    Cement* cement3 = *mutableRef; // <-- OK
    return 0;
}


* Just found www.ideone.com (AMAZING)
Last edited on
ty LL - I like it - having bad code fail at compile time is excellent

I've added to your code and tried some things out.

Do you have any suggestions on how to better implement Line 22's throw (better alternatives?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>

using namespace std;

template<typename T>
class MutableReference
{
  private:
    T* object_;
  public:

    MutableReference(T* object) : object_(object) { }

    MutableReference(void) : object_(0) { }

    T* operator*(void)
    {
      if(object_->canMutate())
      {
        return this->object_;
      } else {
        throw;
      }
    }

    ~MutableReference(void) { }
};

template<typename T>
class ImmutableReference
{
  private:

    T* object_;
  public:

    ImmutableReference(T* object) : object_(object) { }

    ImmutableReference(void) : object_(0) { }

    const T* operator*(void) const
    {
      return this->object_;
    }

    MutableReference<T> lockMutation(void) const
    {
      MutableReference<T> rv;
      if(object_->canMutate())
      {
        rv= MutableReference<T>(object_);
      }
      return rv;
    }

    ~ImmutableReference(void) { }
};

class Cement
{
  public:
    Cement( int i ) : i_( i ), canmutate_( true ) { }
    bool canMutate(void) const { return canmutate_; }
    void writeToDB() { canmutate_ = false; }
    void readFromDB() { canmutate_ = false; }
    void setI( int i ) { i_ = i; }
    int getI() const { return i_; }
  private:
    int i_;
    bool canmutate_;
};

int main()
{
  ImmutableReference<Cement> ref(new Cement( 7 ));
  //    Cement* cement= *ref; // <-- Error, cannot convert const Cement* to Cement*
  // (*ref)->setI( 9 ); // <-- Error: immutable
  cerr << (*ref)->getI() << endl; // <-- OK
  const Cement* cement2 = *ref; // <-- OK
  MutableReference<Cement> mutableRef = ref.lockMutation();
  (*mutableRef)->setI( 9 ); // <-- OK
  (*mutableRef)->writeToDB(); // <-- OK
  (*mutableRef)->setI( 5 ); // <-- Error: immutable
  cerr << (*mutableRef)->getI() << endl; // <-- OK
  Cement* cement3 = *mutableRef; // <-- OK
  return 0;
}

ok, I've refactored the code - three things I don't like about my version though:

1. the throw on Line 28 (need a better way to tell me where to fix the code)
2. getMutable() is not as nice as an operator*(), but there seems to be no way to wedge it in
3. the canmutate_ bool variable is boiler-plate code that's going to be all over the place - in its current form, I will have to copy and paste all over the place - perhaps I need to change it to a superclass or a template CanMutate<>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>

using namespace std;

// a Mutant may be mutable or immutable, depending on its recent interactions with the database
template<typename T>
class Mutant
{
  private:
    T* object_;
  public:

    Mutant(T* object) : object_(object) { }

    Mutant(void) : object_(0) { }

    const T* operator*(void) 
    {
      return this->object_;
    }

    T* getMutable(void)
    {
      if(object_->canMutate())
      {
        return this->object_;
      } else {
        throw;
      }
    }

    ~Mutant(void) { }
};

class MyObject
{
  public:
    MyObject( int i ) : i_( i ), canmutate_( true ) { }
    bool canMutate(void) const { return canmutate_; }
    void writeToDB() const {
      // do work here
      canmutate_ = false;
    }
    void readFromDB() const {
      // do work here
      canmutate_ = false;
    }
    void setI( int i ) { i_ = i; }
    int getI() const { return i_; }
  private:
    int i_;
    mutable bool canmutate_;
}

int main()
{
  Mutant<MyObject> ref(new MyObject( 7 ));
  cerr << (*ref)->getI() << endl; // <-- OK
  ref.getMutable()->setI( 9 ); // <-- OK
  cerr << (*ref)->getI() << endl; // <-- OK
  const MyObject* mo1 = *ref; // <-- OK
  // MyObject*       mo2 = *ref; // <-- Error, no operator*() for accessing mutable
  (*ref)->writeToDB(); // <-- OK
  // (*ref)->setI( 11 ); // <-- Error: immutable
  cerr << (*ref)->getI() << endl; // <-- OK
  const MyObject* mo3( *ref ); // <-- OK
  cerr << mo1->getI() << endl; // <-- OK
  cerr << mo3->getI() << endl; // <-- OK
  return 0;
}
Last edited on
refactored some more, but still have 2 bad smells

1. the throw on Line 27 (need a better way to tell me where to fix the code)
2. getMutable() is not as nice as an operator*(), but there seems to be no way to wedge it in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>

using namespace std;

// a Mutant may be mutable or immutable, depending on its recent interactions with the database
template< typename T >
class Mutant
{
  private:
    T* object_;

  public:
    Mutant( T* object ) : object_( object ) { }
    Mutant( void ) : object_( 0 ) { }

    const T* operator*(void) 
    {
      return this->object_;
    }

    T* getMutable(void)
    {
      if( object_->canMutate() )
      {
        return this->object_;
      } else {
        throw;
      }
    }

    ~Mutant(void) { }
};

class Mutatable
{
  public:
    Mutatable() : canMutate_( true ) { }
    void setMutatable( bool mutatable ) const { canMutate_=mutatable; }
    bool canMutate() const { return canMutate_; }

  private:
    mutable bool canMutate_;
};

class MyObject : public Mutatable
{
  public:
    MyObject( int i ) : i_( i ) { }
    void writeToDB() const {
      // do work here
      setMutatable( false );
    }
    void readFromDB() const {
      // do work here
      setMutatable( false );
    }
    void setI( int i ) { i_ = i; }
    int getI() const { return i_; }
  private:
    int i_;
};

int main()
{
  Mutant<MyObject> ref( new MyObject( 7 ));
  cerr << (*ref)->getI() << endl; // <-- OK
  ref.getMutable()->setI( 9 ); // <-- OK
  cerr << (*ref)->getI() << endl; // <-- OK
  const MyObject* mo1 = *ref; // <-- OK
  // MyObject*       mo2 = *ref; // <-- Error, no operator*() for accessing mutable
  (*ref)->writeToDB(); // <-- OK
  // (*ref)->setI( 11 ); // <-- Error: immutable
  cerr << (*ref)->getI() << endl; // <-- OK
  const MyObject* mo3( *ref ); // <-- OK
  cerr << mo1->getI() << endl; // <-- OK
  cerr << mo3->getI() << endl; // <-- OK
  return 0;
}
I'm assuming this isn't just a temporary block while a background thread read/writes to a database, but a single thread where in memory objects are being written to a database for memory/speed requirements.

I don't really understand the use case for this. If I am in a piece of code which has a mutatable object that for whatever reason I want to mutate - what am I expected to do when I find that the object can not be mutated (ie when I catch the exception), and is not likely to change status if I wait? Can you give me a real example where throwing an exception and letting the calling code handle it is the best solution?

Surely the best thing is to implement copy on write, or some similar idea?
kev82, thanks for your thoughtful insight - copy on write didn't cross my mind when I was considering my requirements, but let me think a little bit - it certainly might be the most viable approach...

I have two places where I need this kind of functionality:

1. In a DB time-machine scenario when I keep track of all variants of a object (lets call each of these variants a record or a row in the DB) over time - this way, I can always query the database anywhere on this time-axis and be able to retrieve the state of the system any time in the past (hence, the time-travel name). In this kind of scenario, all variants are immutable - you may try to change the latest variant, but doing so should create a copy - a new mutable instance. (in this case, I think copy on write may work just fine)

2. In an accounting system, we have to use Decimal/Money arithmetic. One model is, we can use double or any high-precision float to do calculations, but once a figure shows up in a report (or is written to the database), we must convert back to a Decimal/Money object for real pennies (all pennies must add up!). This means when a Money amount is written to the DB, it is a fungible immutable quantity. We can do calculations with this amount using a mutable object, but once it's written out to the database, that instance becomes immutable.

1. and 2. are not mutually exclusive (2. can be implemented in terms of 1., in fact).

Let me see if I can rewrite the code with copy on write in mind (I will copy on write if the current instance is immutable - if it's already mutable, I don't mind) - ty!
Last edited on
kev82, how do you propose I implement copy on write? In every one of my setters, check to see if I need to make a copy before modifying? Seems kind of messy - I'm open to ideas, though...

I thought about your comment regarding the throw, though - I will replace it with a copy and return the mutable copy - that should work much better...
For situation 1, I would just keep them mutable all the time, but not have them encode a time component, keep that as a separate entity.

So your database store/retrieve functions would look like

commitState(std::pair<TimeOfState,State> p)
State RetrieveState(TimeOfState t)

The state objects are always mutable, which makes sense as they don't have an innate point of time.

If the State objects are large then it would be quicker (but not necessary) to implement copy on write.

In situation 2 you only have a problem if you try and maintain a 1-1 correspondence between the variable in memory and the database record. Why do you need this correspondence? When you have calculated the amount write it, and if you ever need to look it up, call a database function to find it. If you need to look it up a lot, cache the database function.

Just my opinion, but better to remove the problem in the first place, than to find a way around it. In both these cases you never need to deal with an immutable object.
hmm... ...I am probably not making my requirements clear

suppose I have the following records:

RecNo|LastName|FirstName|State|dt_from|dt_end
1|Smith|John|CA|1-1-2001|5-15-2009
2|Smith|John|FL|5-16-2009|5-31-2011
3|Smith|John|ME|6-1-2011|12-31-9999
...

I clearly do not want RecNo 1 or 2 to be mutable when I pull them into memory.

In fact, even RecNo 3 is immutable in the program, in this sense - if the program wants to change John Smith's state to NY on 6-5-2011, I would make a copy of RecNo 3 and update the database this way:

3|Smith|John|ME|6-1-2011|6-4-9999
4|Smith|John|NY|6-5-2011|12-31-9999

the dt_end for RecNo 3 has changed, but its guts (RecNo|LastName|FirstName|State) have not.

RecNo 3's guts are immutable once written to or read from the database.

The immutability is not a problem, it's a requirement for the project to prevent modification of historical records.




here's the latest version - throw has been removed from getMutable() - instead, we make a copy and return a mutable instance

Mutant<> will also now keep track of all variants over time and do clean-up upon destruction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <assert.h>
#include <iostream>
#include <vector>

using namespace std;

// a Mutant may be mutable or immutable, depending on its recent interactions with the database
// the Mutant will delete all its objects for you
template< typename T >
class Mutant
{
  private:
    vector< T* > objects_;

  public:
    Mutant( T* object ) { objects_.push_back( object ); }
    Mutant( void ) { }
    ~Mutant( void ) {
      size_t i, n=objects_.size();
      for ( i=0; i<n; ++i )
        delete objects_[i];
    }

    size_t size() const { return objects_.size(); }

    const T* operator*(void) 
    {
      unsigned n = objects_.size();
      if ( !n ) return NULL;
      return objects_[ n-1 ];
    }

    T* getMutable(void)
    {
      unsigned n = objects_.size();
      if ( !n ) return NULL;
      if( objects_[ n-1 ]->canMutate() )
      {
        return objects_[ n-1 ];
      } else {
        objects_[ n-1 ]->setMutatable( false );
        objects_.push_back( new T( *objects_[ n-1 ] ));
        return objects_[ n ];
      }
    }
};

class Mutatable
{
  public:
    Mutatable() : canMutate_( true ) { }
    void setMutatable( bool mutatable ) const { canMutate_ = mutatable; }
    bool canMutate() const { return canMutate_; }

  private:
    mutable bool canMutate_;
};

// MyObject only needs to subclass from Mutatable and call setMutatable( false ) when appropriate
class MyObject : public Mutatable
{
  public:
    MyObject( int i ) : i_( i ) { }
    MyObject( const MyObject& rhs ) : i_( rhs.i_ ) { }
    void writeToDB() const {
      // do work here
      setMutatable( false );
    }
    void readFromDB() const {
      // do work here
      setMutatable( false );
    }
    void setI( int i ) { i_ = i; }
    int getI() const { return i_; }
  private:
    int i_;
};

int main()
{
  Mutant<MyObject> ref( new MyObject( 7 ));
  assert( (*ref)->getI()==7 ); // <-- OK

  ref.getMutable()->setI( 9 ); // <-- OK
  assert( (*ref)->getI()==9 ); // <-- OK

  (*ref)->writeToDB(); // <-- OK: ref is now immutable
  const MyObject* mo1 = *ref; // <-- OK
  MyObject*       mo2 = ref.getMutable(); // <-- OK, makes a copy since last is immutable
  mo2->setI( 11 );  // <-- OK
  assert( (*ref)->getI()==11 ); // <-- OK

  (*ref)->writeToDB(); // <-- OK: ref is now immutable
  // (*ref)->setI( 11 ); // <-- Error: immutable
  assert( (*ref)->getI()==11 ); // <-- OK

  const MyObject* mo3( *ref ); // <-- OK
  assert( mo1->getI()==9  ); // <-- OK
  assert( mo3->getI()==11 ); // <-- OK

  assert( ref.size()==2 ); // <-- OK
  return 0;
}


edit: thx to LL & kev82 for your ideas and your code - I'm going to mark this one solved
Last edited on
I think I understood your problem, but I don't think you understood what I meant, so just to clarify.

With your example, lets say this table stores the manager of a project at a particular time, given by dt_start and dt_finish.

You then have one object, Person which looks like

struct Person
{
firstName,
lastName,
state
};

You then put into database with

setManager(Person p, Date from, Date to);
Person retrieveManager(Date date);

Your person objects are always mutable because they have no direct correspondence with a database row.

You can throw from serManager, if the dates provided would make a change that is not allowed.

If Person objects are really big, then you could do copy on write.

But as long as your happy and have a design that works for you, then that's all that really matters.
Topic archived. No new replies allowed.