3

In RAII, resources are not initialized until they are accessed. However, many access methods are declared constant. I need to call a mutable (non-const) function to initialize a data member.

Example: Loading from a data base

struct MyClass
{
  int get_value(void) const;

  private:
     void  load_from_database(void); // Loads the data member from database.

     int m_value;
};

int
MyClass ::
get_value(void) const
{
  static bool value_initialized(false);
  if (!value_initialized)
  {
    // The compiler complains about this call because
    // the method is non-const and called from a const
    // method.
    load_from_database();
  }
  return m_value;
}

My primitive solution is to declare the data member as mutable. I would rather not do this, because it suggests that other methods can change the member.

How would I cast the load_from_database() statement to get rid of the compiler errors?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • 1
    make sure to inform the user that the first call of your getXXX functions might take some time ... Or users might call that function the first time in a critical section where performance is important... – smerlin Mar 19 '10 at 16:47
  • 3
    @Thomas Matthews,here's a link explaining what RAII actually is http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization – Glen Mar 19 '10 at 16:47
  • You could/should provide an additional init-functions that calls every getXXX function once ... so users can call that function if they dont want delayed-initialization – smerlin Mar 19 '10 at 16:50
  • 1
    "In RAII, resources are not initialized until they are accessed." RAII stands for "Resource *acquisition* Is Initialization", so your first statement is false. – John Dibling Mar 19 '10 at 17:01
  • 1
    That `static bool value_initialized(false);` not only reminds of 'most vexing parse', but is _shared between all instances of MyClass_. Is that desired? – mlvljr Mar 19 '10 at 17:21
  • 1
    As @John said, in RAII; resources are acquired **when the object is initialized**. That's what the name *means*. – jalf Mar 19 '10 at 17:27
  • 3
    RAII is really "Using C++ Destructor Semantics To Manage Resources" (UCDSTMR), see http://stackoverflow.com/questions/712639/please-help-us-non-c-developers-understand-what-raii-is/712649#712649 – Daniel Daranas Mar 19 '10 at 18:10
  • 1
    @Daniel: That's the other half of it, yes. RAII really covers both constructor and destructor. It is the idea that the resource lifetime should be mapped to the object: if the object exists, so does the resource. When the object is created, the resource is acquired, and when the object is destroyed, the resource is released. Both construction and destruction are vital for proper RAII. – jalf Mar 19 '10 at 19:05
  • @mlvljr: The variable is local to the function (method) and initialized before `main()` because of the `static` qualifier. Thus it is a good flag for initialization. All instances will use it indirectly, as it indicates whether the value was initialized. – Thomas Matthews Mar 19 '10 at 19:32

6 Answers6

20

This is not RAII. In RAII you would initialize it in the constructor, which would solve your problems.

So, what you are using here is Lazy. Be it lazy initialization or lazy computation.

If you don't use mutable, you are in for a world of hurt.

Of course you could use a const_cast, but what if someone does:

static const MyClass Examplar;

And the compiler decides it is a good candidate for Read-Only memory ? Well, in this case the effects of the const_cast are undefined. At best, nothing happens.

If you still wish to pursue the const_cast route, do it as R Samuel Klatchko do.

If you thought over and think there is likely a better alternative, you can decide to wrap your variable. If it was in class of its own, with only 3 methods: get, set and load_from_database, then you would not worry about it being mutable.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
5

You are basically implementing a caching mechanism. Personally I think it's OK to mark cached data as mutable.

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
5

As Matthieu already pointed out, what you're trying to do here has little (if anything) to do with RAII. Likewise, I doubt that any combination of const and mutable is really going to help. const and mutable modify the type, and apply equally to all access to an object of that type.

What you seem to want is for a small amount of code to have write access, and anything else only read access to the value. Given the basic design of C++ (and most similar languages), the right way to do that is to move the variable into a class of its own, with the small amount of code that needs write access as part of (or possibly a friend of) that class. The rest of the world is given its read-only access via the class' interface (i.e., a member function that retrieves the value).

The (presumably stripped down) MyClass you've posted is pretty close to right -- you just need to use that by itself, instead of as part of a larger class with lots of other members. The main things to change would be 1) the name from MyClass to something like lazy_int, and 2) (at least by my preference) get_value() should probably be renamed to operator int(). Yes, m_value will probably need to be mutable, but this doesn't allow other code to write the value, simply because other code doesn't have access to the value itself at all.

Then you embed an object of that type into your larger class. The code in that outer class can treat it as an int (on a read-only basis) thanks to its operator int(), but can't write it, simply because the class doesn't give any way to do so.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
4

[ LOOK MA! NO CASTS! :)) ]

struct DBValue 
{
  int get_value();

private:
  void load_from_database();
  int value;
};

struct MyClass 
{
  MyClass(): db_value(new DBValue()) {}
  ~MyClass() { delete db_value; } 

  int get_value() const;

private:
  DBValue * const db_value;
};

int MyClass::get_value() const
{
  return db_value->get_value(); // calls void load_from_database() if needed
}

The idea is to have a politically correct MyClass with const methods not mutating anything but calling both const and non-const methods of aggregated objects via const pointers.

mlvljr
  • 4,066
  • 8
  • 44
  • 61
1

Don't use const_cast here, or you're asking for trouble. Using mutable in this case shouldn't be a problem, but if the profiler didn't suggest otherwise then I think users would be less surprised to see an object that is expensive to construct than an accessor method that is expensive to call the first time.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
0

If your method changes the state of the object (e.g. by changing the state of the underlying database), then the method should not be const. In that case you should have a separate, non-const load-method, that has to be called before the const getter can be called.

This method would require neither const_cast not mutable, and would make the potentially expensive operation explicit.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • The theme of the issue is that the variable is initialized using `lazy initialization`, which is the first time it is accessed. All future accesses to this variable are read-only. The database is not changed, only the order of initialization. – Thomas Matthews Mar 19 '10 at 19:39