While debugging crash in a multithreaded application I finally located the problem in this statement:
CSingleLock(&m_criticalSection, TRUE);
Notice that it is creating an unnamed object of CSingleLock class and hence the critical section object gets unlocked immediately after this statement. This is obviously not what the coder wanted. This error was caused by a simple typing mistake. My question is, is there someway I can prevent the temporary object of a class being created at the compile time itself i.e. the above type of code should generate a compiler error. In general, I think whenever a class tries to do some sort of resource acquisition then the temporary object of that class should not be allowed. Is there any way to enforce it?
Explanation: The temporary object is not created. If object is returned by reference, a particular memory location will be denoted with another name and hence same address values will be used.
Temporary objects are those that have a short lifetime and generally serve no useful purpose other than to act as containers for other data. Programmers generally use temporary objects to pass compound data to -- or return it from -- a method.
Edit: As j_random_hacker notes, it is possible to force the user to declare a named object in order to take out a lock.
However, even if creation of temporaries was somehow banned for your class, then the user could make a similar mistake:
// take out a lock: if (m_multiThreaded) { CSingleLock c(&m_criticalSection, TRUE); } // do other stuff, assuming lock is held
Ultimately, the user has to understand the impact of a line of code that they write. In this case, they have to know that they're creating an object and they have to know how long it lasts.
Another likely mistake:
CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE); // do other stuff, don't call delete on c...
Which would lead you to ask "Is there any way I can stop the user of my class from allocating it on the heap"? To which the answer would be the same.
In C++0x there will be another way to do all this, by using lambdas. Define a function:
template <class TLock, class TLockedOperation> void WithLock(TLock *lock, const TLockedOperation &op) { CSingleLock c(lock, TRUE); op(); }
That function captures the correct usage of CSingleLock. Now let users do this:
WithLock(&m_criticalSection, [&] { // do stuff, lock is held in this context. });
This is much harder for the user to screw up. The syntax looks weird at first, but [&] followed by a code block means "Define a function that takes no args, and if I refer to anything by name and it is the name of something outside (e.g. a local variable in the containing function) let me access it by non-const reference, so I can modify it.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With