Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart pointer that lazily recreates its resource

I have a ServiceProvider class which contains a couple of pointers to different services, like that:

class ServiceProvider()
{
    Service3* GetService3();

public:
    void Process(Object* o);
    void Shrink();

private:
    TAutoSpawningPtr<Service1> service1;
    TAutoSpawningPtr<Service2> service2;

    Service3* service3;
}

Note that TAutoSpawningPtr is a theoretical smart pointer сlass I'm looking for, and service3 is declared as an ordinary pointer to explicitly show the behaviour I needed. The body of Process():

void ServiceProvider::Process(Object* o)
{
    service1->Process(o);
    service2->Process(o);
    GetService3()->Process(o);
}

The body of GetService3():

void ServiceProvider::GetService3()
{
    if(!service3)
    {
       service3 = new Service3();
    }

    return service3;
}

As you can see, an instance of Service3 is being created lazily and it don't exist until it needed.

Shrink() method is being called periodically to delete all internal services. Like this:

void ServiceProvider::Shrink()
{
    service1.Release(); // delete its internal Service1 pointer if it exists.
    service2.Release(); // delete its internal Service2 pointer if it exists.

    if (service3)
    {
        // delete its internal Service3 pointer if it exists.
        delete service3;
        service3 = nullptr;
    }
}

What do I need: I want TAutoSpawningPtr<> to be a smart pointer class, which automatically creates its class instance by calling the default construcror once I dereference the pointer using an overloaded operator->. An inner resource posessed by the pointer had to be deleted once called the Release() method (and, of course, it had to be recreated when I need it again).

Why do I need this?

  1. To automatically control presence/absence of an object.
  2. To prevent nullptrs when derefenecing pointers directly (like this->service3->Process(o)) instead of indirect GetService3().
  3. To release unused services without explicit checks.

The question is: Does the standard (or any third-party) library have an auto pointer class which will satisfy my needs? And if not, would you kindly to bring me some code examples that shows behavior I need. Thanks.

like image 392
Netherwire Avatar asked Jun 28 '17 08:06

Netherwire


1 Answers

The simplest solution here would be to just call a function that initializes the two if they are uninitialized or are not pointing to anything.

But if you really want to, you can create a simple proxy pointer class that does this for you. For example

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

class Something {
public:
    Something() {
        cout << __PRETTY_FUNCTION__ << endl;
    }
    void do_something() {
        cout << __PRETTY_FUNCTION__ << endl;
    }
};

template <typename Type,
          template <typename...> class Ptr = std::unique_ptr>
class AutoAllocatingPtr {
public:
    Type* operator->() {
        if (!this->ptr) {
            this->ptr = Ptr<Type>{new Type{}};
        }
        return this->ptr.get();
    }

    void release() {
        this->ptr.reset();
    }

private:
    Ptr<Type> ptr;
};

int main() {
    // default unique ownership with std::unique_ptr
    auto ptr = AutoAllocatingPtr<Something>{};
    ptr->do_something();
    ptr.release();
    ptr->do_something();

    // if you want shared ownership
    auto s_ptr = AutoAllocatingPtr<Something, std::shared_ptr>{};
    s_ptr->do_something();
    s_ptr.release();
    s_ptr->do_something();
}

Note Note the code in the end and how you can use that to switch the type of ownership semantics that the pointer exhibits.

like image 166
Curious Avatar answered Oct 04 '22 19:10

Curious