Skip to content

Implementation Patterns

René Fonseca edited this page Feb 6, 2020 · 8 revisions

Description of common implementation patterns.

On demand initialization

Use the compiler to ensure MT-safety for on-demand initialization of global resources.

Reference<Resource> create()
{
  Reference<Resource> resource = new Resource();
  // do something
  if (gotError) {
    return nullptr;
  }
  return resource;
}

Reference<Resource> getResource()
{
  static Reference<Resource> resource = create(); // compiler will ensure MT-safety here
  return resource;
}

ATTENTION: You should ensure that globals do not have cyclic dependencies. This can cause a runtime crash or deadlock.

Resources

Use an internal reference counted handle to avoid issues with copying by value. It is generally undesired to throw exceptions in destructors - so it is always good to have a way to avoid this possibility by providing a way to detach the handle before destruction. Similarly it is good to have the option to construct invalid handles and delay actual resource acquisition until a later time.

class Resource : public Object {
private:

  class Handle : public ReferenceCountedObject {
  };

  Reference<Handle> handle;
public:

  /** Could create resource or initialize as invalid. */
  Resource();

  /** Allow check if resource is valid if it can be explicitly closed. */
  bool isValid() const;

  /** Creates the resource. */
  void open();

  void useit();

  /** Close handle. Impacts all usages. */
  void close();
};

void doit()
{
  Resource r1;
  Resource r2 = r1; // fine since we use reference counting
  r2.useit();
  r2.close();
  r1.useit(); // should fail since closed
  r2.close(); // multiple closes should be allowed
}

When adding new resources, you should consider adding direct tracking support in the Profiler also.

Clone this wiki locally