NoesisGUI

Smart Pointers in Noesis

As described in the Reference Counting section Noesis Components inherit from the class BaseRefCounted and they automatically incorporate a reference counting for controlling its lifetime.

Ptr<> is a template that is used to automatize the process of adding and releasing references to components. Ptr<> acts as a smart pointer. This means that it is used in the same way that pointers are used and when the Ptr is copied references are automatically incremented.

Casting between classes is done using RTTI information that is stored in each component.

Let see how this works in a example. We are going to use a component (sphere) with two interfaces (IVisible and IFloat) and a component (box) without interfaces.

//////////////////////////////////////////////////////////////////////////
NS_INTERFACE IVisible: public Interface
{
    virtual void Hide() = 0;
    virtual void Show() = 0;

    NS_IMPLEMENT_INLINE_REFLECTION_(IVisible, Interface)
};

//////////////////////////////////////////////////////////////////////////
NS_INTERFACE IFloat: public Interface
{
    virtual void SetFloat(NsFloat32 value) = 0;
    virtual NsFloat32 GetFloat() = 0;

    NS_IMPLEMENT_INLINE_REFLECTION_(IFloat, Interface)
};

//////////////////////////////////////////////////////////////////////////
class Sphere: public BaseComponent, public IVisible, public IFloat
{
public:
    Sphere()
    {
        numSpheres++;
    }

    ~Sphere()
    {
        numSpheres--;
    }

    /// From IVisible
    //@{
    void Hide() {}
    void Show() {}
    //@}

    /// From IFloat
    //@{
    void SetFloat(NsFloat32 value)
    {
        NS_UNUSED(value);
    }

    NsFloat32 GetFloat()
    {
        return 0.0f;
    }
    //@}

    NS_IMPLEMENT_INLINE_REFLECTION(Sphere, BaseComponent)
    {
        NsImpl<IVisible>();
        NsImpl<IFloat>();
    }
};

//////////////////////////////////////////////////////////////////////////
class Box: public BaseComponent
{
public:
    Box() {}
    ~Box() {}

    NS_IMPLEMENT_INLINE_REFLECTION_(Box, BaseComponent)
};

And now, let's create a sphere component. Component can be created like normal classes with the operator new, or using the Kernel Factory. To simplify, here we will use the operator new.

// Create the component. Reference count is equal to 1
Ptr<Sphere> sphere = *new Sphere;

// Cast to the IVisible interface (reference count incremented to 2)
Ptr<IVisible> visible = NsStaticCast<Ptr<IVisible> >(sphere);
visible->Hide();
visible->Show();

// Cast to the IFloat interface (reference count is incremented to 3)
Ptr<IFloat> flt = NsDynamicCast<Ptr<IFloat> >(visible); // static cast is not possible
flt->SetFloat(1.0f);
flt->GetFloat();

// Here, we try to cast to Box. But Sphere does not implement the Box class. A null pointer
// is returned here and the reference count is left intact
Ptr<Box> box = NsDynamicCast<Ptr<Box> >(sphere);

// Calling Reset is the way to release the reference managed by the Ptr. If you do not do this,
// the reference is automatically decremented when sphere goes out of scope.
// NEVER EVER do delete sphere!
sphere.Reset();

// Here, the component sphere created at the beginning is automaticaly destroyed

Ptr<> automatically manages the memory. You should not destroy components using delete. This behaviour is unsupported and will crash your program (although there are asserts detecting this in Debug configuration).

Weak Pointers

A Ptr<> represents a hard reference. If you need a weak reference (more information about weak references in Reference Counting) you can assign a Ptr<> to a WeakPtr<> (of course only for types that support WeakReferences. To support WeakReferences a component must inherit from BaseWeakReferenceable instead of BaseComponent)

Ptr<Sphere> sphere = *new Sphere(20.0f);
WeakPtr<Sphere> w0 = sphere;

NS_UNITTEST_CHECK(sphere->GetNumReferences() == 1);

Optimization tricks

Whenever posible and with the idea of avoiding incrementing and decrementing references when it is not necessary, raw pointers must be passed and returned instead of Ptr. For example

void AddMeshes(IMesh* m0, IMesh* m1);

class Node
{
public:
   INode* GetChild(NsSize i);
};

To avoid the dynamic cast using the component RTTI information, you can use the template NsStaticCast

// Only use this if you ARE SURE that flt is a class or derived class from Sphere
// To detect this NsStaticCast is implemented with NsDynamicCast in debug configurations

// IFloat* flt;
Sphere* sphere = NsStaticCast<Sphere*>(flt);

Ptr<> const correctness

Ptr<> implements the two kind of constness you can apply to a normal pointer:

  • Constant Pointer (T* const): this is expressed in a Ptr with the const operator applied like in other classes
const Ptr<MyInterface> obj;
  • Object pointed to is constant (const T*): this is expressed in a Ptr applying the const to the type T of the Ptr
Ptr<const MyConstInterface> obj;
© 2017 Noesis Technologies