Programming Guide


Temporary References and Objects

 

When writing OpenDoc-based code, one often needs to create temporary objects that need to be freed, or acquire temporary references to objects that then need to be released. The following are pitfalls:

This results in a memory or ref-count leak, unless you put in an exception handler whose job is to clean up these temporaries, which complicates your code and introduces even more room for subtle errors.

Here is an example of code that uses a temporary reference:

{
  ODShape *s = frame->GetFrameShape(ev,kODNULL);
  DoSomethingTo(s);
  s->Release(ev);
}

We had to remember to call Release on object s after we were through with it. And we still have the problem that, if DoSomething throws an exception, s will be left dangling. We could make the code exception-safe by rewriting it as:

{
  ODShape *s = frame->GetFrameShape(ev,kODNULL);
  try {
      DoSomethingTo(s);
      } catch (ODException_Exception) {
      s->Release(ev);
      throw;
      }
  s->Release(ev);
}

This makes the code more complicated, and you have to repeat the Release call twice. It is easy to get this wrong, resulting in code that works fine unless an exception is thrown, in which case it does the wrong thing. Since exceptions happen rarely in normal use, this results in spurious bugs.

An elegant solution to the problem is to make use of stack-based C++ objects whose destructors will be called whenever they go out of scope, whether through exiting a block normally, or via an exception.

Using the TempObj utility, which can be found in the SAMPLES/SRC/UTILS directory, the routine can be rewritten as:

{
  TempODShape s = frame->GetFrameShape(ev,kODNULL);
  DoSomethingTo(s);
}

All we had to do was change ODShape* to TempODShape, and take out the Release call. The rest is the same. Although object s is now an actual (stack-based) object, not a pointer, it can still be used as though it were an ODShape*. In particular, the following kinds of things are legal and do what you would expect:

s->GetBoundingBox ( . . . )
xform->TransformShape(ev,s);
if (s != kODNULL)  . . .
if (s)  . . .

// Note that this does not release the shape object s used to point to!
s = frame->GetUsedShape(ev,kODNULL);
s = kODNULL;

The Release happens automatically when the block exits or if DoSomething throws an exception. Of course, if s holds a pointer to kODNULL, no Release operation will occur.

Using TempObjs and TempRefs

   

To use this facility, just #include <TempObj.H> in your source files. This gives you access to the following classes:

You can get access to Temp iterator classes by including TempIter.h. This gives you access to the following classes:

(Note that iterators are not ref-counted, so the Temp_Iterator classes delete the iterator object at the end instead of releasing it).

Pitfalls

The biggest mistake you can make in using this utility is forgetting that the object is always released. This can bite you if you need to use the object as the return value of a function:

ODShape *foo (ODFrame *frame)
{
  TempODShape s = frame->GetFrameShape(ev,kODNULL);
  DoSomething(s);
  return s;
}

The ODShape is going to be released before it is returned, when the destructor of object s is called. This is bad news, since the function will return either a pointer to a deleted object, or to an object whose ref-count is one too low. Either case is likely to cause a crash. It is still nice to use a TempODShape in this function, for safety in case DoSomething throws an exception. We just want to tell object s not to release itself when it is being returned. You can do this by setting the shape to kODNULL before returning it:

ODShape *foo (ODFrame *frame)
{
  TempODShape s = frame->GetFrameShape(ev,kODNULL);
  DoSomething(s);
  ODShape *temp = s;
  // object s will not be released by the destructor now
  s = kODNULL;
  return temp;
}

Of course, this is a kludge in that we have to store the value of object s in a temporary to keep it from being lost. But there is a convenience method called DontRelease that will set the reference to NULL but return its previous value:

ODShape *foo (ODFrame *frame)
{
  TempODShape s = frame->GetFrameShape(ev,kODNULL);
  DoSomething(s);
  // Note that we used  ".", not  "->"
  return s.DontDelete();
}

Adding New Classes

There are other classes you might want to have Temp____ objects available for.

You can declare a temporary reference to any type of ref-counted object by using the class TempRef<classname>. For instance:

TempRef<ODDraft> su = doc->AcquireDraft(ev);

You can also use temporary instances of non-ref-counted objects by using TempObj<class>:

TempObj<ODPeanutIterator> iter = peanut->GetIterator(ev);

Note:

Since this defines a template class, you may need to use special compiler options for generating templates. Refer to your compiler documentation.

If, after adding a new class, you get a type-mismatch error in TempObj.H. This may indicate that you are trying to use TempObj with a class that is not a subclass of ODObject, or TempRef with a class that is not a subclass of ODRefCntObject. This can happen even if the class is correct, if the compiler has not seen the declaration of the class before the declaration of the temporary. In other words, the following is wrong:

class ODFoo;
TempRef<ODFoo> ref = . . .;
#include "ODFoo.h"

At the time that the compiler instantiates the template for ODFoo, it does not know anything about the class, such as whether it is a subclass of ODRefCntObject, and will therefore give you type-check errors. You can avoid this by including the header for ODFoo before using the TempRef class.


[ Top | Previous | Next | Contents | Index | Documentation Homepage ]