The following observer class registers a callback function with a subject via a call to attach when its ctor executes. I would like the observer's dtor to unregister the callback function.

How do I pass the same pointer to detach as I did to attach so that the subject can remove it from its list of observers?

I'm thinking I have to store a shared_ptr to this but I'm not sure how I get from that to the shared_ptr to the member function callback.

Can anyone help?

Observer::Observer(Subject& subject) : m_subject(subject),
{
    m_subject.attach("cmd", std::make_shared<std::function<void()>>(std::bind(&Observer::callback, this)));
}

Observer::~Observer()
{
    // I tried this initially but realised that the pointer below
    // is different to the one passed to `attach` from the ctor.

    m_subject.detach("cmd", std::make_shared<std::function<void()>>(std::bind(&Observer::callback, this)));
}

void Observer::callback()
{
    // do some stuff
}

attach and detach are declared as follows:

void Subject::attach(const std::string& command, const std::shared_ptr<std::function<void()>>& callback);

void Subject::detach(const std::string& command, const std::shared_ptr<std::function<void()>>& callback);

2 Answers

1
Superlokkus On Best Solutions

I would suggest to also store a copy of the shared_ptr in your observer class, as a member perhaps. Then you could you it to give the same pointer to detach.

Like this:

class Observer {
    //Other Stuff here

private:
    std::shared_ptr<std::function<void()>> callback_ptr;
};

Observer::Observer(Subject& subject) : 
m_subject(subject), 
callback_ptr(std::make_shared<std::function<void()>>(std::bind(&Observer::callback, this))
{
    m_subject.attach("cmd", std::make_shared<std::function<void()>>(std::bind(&Observer::callback, this)));
}

Observer::~Observer()
{
    m_subject.detach("cmd", callback_ptr));
}

It's worth noting that, as @dkg also mentioned, make_shared does always return a new shared_ptr for every time it's called, because it's purpose is to "create a new object with the following parameters and return a shared_ptr to it". Is roughly equivalent to shared_ptr(new std::function<void()> (std::bind(&Observer::callback, this)). The reason why there is an extra make_shared function is to minimize allocations and to guarantee exception safety in a long expression. If you just want to shared one object, then just distribute a copy of your shared_ptr.

1
dkg On

Each time you call std::make_shared it allocates and constructs a new object for you.

In your code, you call it twice, then you have two instances of your object that are not the same.

You could call it once and keep the shared pointer as a member of your class.