Goes without saying: Crash safety

by .

CINEMA 4D is known for its stability. While other 3D packages sometimes tend to randomly crash on you several times a day, CINEMA 4D runs stable and does not crash very often.

When you develop plugins for CINEMA 4D, you want to add functionality without making the whole application instable.

Page 1 | Page 2 | Page 3 | Page 4

Null Pointer checks

One of the most common reasons for crashes are NULL pointers that are accessed without checking first.

Allocating objects

When you allocate an instance of a class, you should always check for NULL pointers! This little check does not cost you much time but effectively prevents crashes. The point is, you can’t just believe that something has been successfully allocated, you have to know it. If the computer is running out of memory, or if the memory is highly fragmented, and you’re allocating a huge amount of memory, allocation might fail.

BaseObject* CreatePlane(Real width, Real height)
{
  BaseObject* op = BaseObject::Alloc(Oplane);
  BaseContainer* bc = op->GetDataInstance();
  bc->SetReal(PRIM_PLANE_WIDTH, width);
  bc->SetReal(PRIM_PLANE_HEIGHT, height);
  return op;
}

// ... later ...

BaseDocument* doc = GetActiveDocument();
BaseObject* plane = CreatePlane(RCO 200.0, RCO 300.0);

plane->SetName("My new plane");
doc->InsertObject(plane, NULL, NULL);

This code might work fine, but it might also just crash on some machines. Can you see how many potential crashes we have in this code? If you think “4”, you are right. There are four locations that could cause a crash.

BaseObject* CreatePlane(Real width, Real height)
{
  BaseObject* op = BaseObject::Alloc(Oplane);
  BaseContainer* bc = op->GetDataInstance();  // Access a member function of "op" without knowing if "op" has been allocated!
  bc->SetReal(PRIM_PLANE_WIDTH, width);       // Access a member function of "bc" without knowing if "bc" is valid!
  bc->SetReal(PRIM_PLANE_HEIGHT, height);
  return op;
}

// ... later ...

BaseDocument* doc = GetActiveDocument();
BaseObject* plane = CreatePlane(RCO 200.0, RCO 300.0);

plane->SetName("My new plane");               // Access a member function of "plane" without knowing if "plane" has been allocated!
doc->InsertObject(plane, NULL, NULL);         // Access a member function of "doc" without knowing if "doc" is valid

Even if it might seem unlikely that one of these situations might occur, it is possible. Adding some extra checks won’t hurt anybody, won’t decrease performance and effectively prevent your plugin from crashing:

BaseObject* CreatePlane(Real width, Real height)
{
  BaseObject* op = BaseObject::Alloc(Oplane);
  if (!op) return NULL;                        // Cancel if "op" has not been allocated

  BaseContainer* bc = op->GetDataInstance();
  if (!bc)                                     // Check if "bc" has been successfully retrieved
  {                                              // if not...
    BaseObject::Free(op);                        // ...free the already allocated "op"
    return NULL;                                 // ...and cancel
  }

  bc->SetReal(PRIM_PLANE_WIDTH, width);
  bc->SetReal(PRIM_PLANE_HEIGHT, height);

  return op;
}

// ... later ...

BaseDocument* doc = GetActiveDocument();
if (!doc) return;      // If there's no document at all, cancel (from whatever function we're in)

BaseObject* plane = CreatePlane(RCO 200.0, RCO 300.0);
if (!plane) return;    // If no plane has been created, cancel

plane->SetName("My new plane");
doc->InsertObject(plane, NULL, NULL);

Voilà, the code is now crash safe.

Allocating memory

Of course, Null Pointer checks should always be performed. Not only when allocating C4D objects, but generally always when memory is allocated for any purpose.

Real* myRealArray = GeAllocType(Real, 4096);
if (!myRealArray) return NULL;  // Check for NULL, in case the allocation was not successful
Advertisements

Pages: 1 2 3 4