PDA

View Full Version : C++ Method Constness


Daniel Pistelli
September 16th, 2009, 15:18
I haven't posted in a while because of the little time I have. Lately I've been writing compilers, disassemblers, emulators, a memory manager, IDA plugins and lots of GUI stuff.

This topic is a bit "controversial", as much as a programming issue can really be controversial...

Constness for C++ methods is provided to guarantee that a certain method won't modify the members of its class. But let's assume that a read method of a thread-safe class needs to modify the mutex used to synchronize read/write access:

Code:
class MyClass
{
QReadWriteLock lock;

void read(char *data, int len) const
{
lock.lockForRead();
}
};

This code snippet won't compile, as the read method calls a non-const method of it's member lock.

It all comes down to the developer's point of view of method constness. My opinion about the topic is that the mutex is an internal feature of the class and that the outer world shouldn't be aware of it. The constness of the read method should guarantee the non-modification of the data which the class represents.

Calling a non-const method from our const method is pretty easy and could be achieved, in theory, through a cast:

Code:
#define CONST_CALL(type, obj) ((type *) & obj)

class MyClass
{
QReadWriteLock lock;

void read(char *data, int len) const
{
CONST_CALL(QReadWriteLock, lock)->lockForRead();
}
};


However the correct way is to use the mutable keyword to declare the QReadWriteLock member.

Code:
mutable QReadWriteLock lock;


Let's assume, however, that there are too many members we'd have to declare as mutable. Here's a little hack to call a non-const method from a const method of the same class. Let's assume that our read method calls a non-const map method.

Code:
class MyClass;

typedef void (MyClass::*const_map_def)() const;

class MyClass
{
// the non-const map method
void _map()
{
}

// the hack
static const_map_def const_map;
static const_map_def init_const_map();
inline void map() const { (this->*const_map)(); }

void read(char *data, int len) const
{
map();
}
};

// in myclass.cpp

const_map_def MyClass::init_const_map()
{
void (MyClass::*ptr)() = &MyClass::_map;
const_map_def constptr;
memcpy(&constptr, &ptr, sizeof (void *));
return constptr;
}

const_map_def MyClass::const_map = MyClass::init_const_map();


I would like to remind those people that would simply declare the <em>read</em> method as non-const that this would affect the code using the class, since in some situations like when being into a certain Model-View-Controller environment the data request methods are const and don't allow the calling of non-const methods. Rather than using mutable or CONST_CALL in the MVC code, it's better to honor constness in our class.

0rp
September 20th, 2009, 07:57
how about const_cast<MyClass*>(this)->_map();

Daniel Pistelli
September 21st, 2009, 12:03
Yes, even better.

Shub-nigurrath
September 27th, 2009, 16:34
this is imho one of the best tutorial I found about the different usage of constness in C++

http://www.possibility.com/Cpp/const.html

it's worth a reading-