Detecting Deletion
Tags: callbacks, cpp, destructors
Published: 2018-08-31 16:52:28

---
title: Detecting Deletion
published: true
description: How to know if a callback destroys you.
tags: callbacks, cplusplus, destructors
---

Suppose you’re implementing a class that uses a callback:

```cpp
class operation {
public:
    struct done_callback {
        virtual ~done_callback();
        virtual void operation_done( operation *op ) = 0;
    };

    operation() { }

    bool perform( done_callback *cb );

private:
    bool success_ = false;
};

bool operation::perform( done_callback *cb ) {
    // ...
    if ( cb != nullptr )
        cb->operation_done( this );
    return success_;
}
```
> **Aside on naming callbacks**
>
> Even though a callback seems like a good candidate for a functor, that is:
> ```cpp
> virtual void operator()( operation *op ) = 0;
> ```
> The problem with this is that it restricts a class from implementing _multiple_ callbacks simultaneously. Hence, it’s better to give callbacks explicit names.

Further suppose we implement `done_callback` like:

```cpp
struct my_callback : operation::done_callback {
    void operation_done( operation *op ) override;
};

void my_callback::operation_done( operation *op ) {
    // ...
    delete this;
    delete op;
}
```
At the end of `operation_done()`, the callback deletes itself and the `operation` now that it’s done. The problem is that `perform()` does:
```cpp
    return success_;                // really: this->success_
```
and so may dump core because the `operation` was deleted by the callback. So the general question is: how can a member function tell whether a callback deleted its object?

### Detecting Deletion

One way to detect deletion is by having the callback signature be either:
```cpp
        virtual bool operation_done( operation *op ) = 0;
        virtual void operation_done( operation *op, bool *deleted ) = 0;
```
The first would return `true` only if the callback deleted its caller; the second could set `*deleted` to `true`.  While either would work, they feel kind of clunky and not general-purpose enough.  Plus they require cooperation from the callback, hence aren’t surefire.

Ideally, we want to know if `~operation()` was called.  Perhaps have the destructor itself set a “destructor called” flag that `perform()` could check.  But how?  It obviously can’t use a data member of `*this` for the flag.  But it _can_ set a flag in `perform()`’s stack frame.

To do this, we need two things:

1. A “delete detector” (in `perform()`’s stack frame) that detects when an object’s destructor has been called.
2. A “delete signaler” (as a data member of `operation`) that runs when `~operation()` has been called and signals the detector.

Here’s the declaration for `delete_signaler`:

```cpp
class delete_detector;

class delete_signaler {
public:
    delete_signaler()  { }

    delete_signaler( delete_signaler const& ) {
        // intentionally do nothing
    }

    ~delete_signaler();

    delete_signaler& operator=( delete_signaler const& ) {
        // intentionally do nothing
        return *this;
    }

    void set_success( bool success );

private:
    delete_detector *detector_ = nullptr;
};
```
> Both the `delete_signaler` copy constructor and assignment operator intentionally do nothing. Specifically, `detector_` _must not_ be copied if and when the `delete_signaler` is. If it _were_ copied, then one of two bad things would happen:
>
> 1. If the `delete_signaler` copy is destroyed _before_ the callback returns, then the embedded `delete_signaler` may invalidate the signal sent (or not sent) by the `delete_signaler` original.
> 2. If the `delete_signaler` copy is destroyed _after_ the callback returns, then `detector_` will likely point to a `delete_detector` that’s since been destroyed.
>
> Note that it’s insufficient simply not to declare either a copy constructor or assignment operator because the compiler will auto-generate both that will copy `detector_`. Hence, both _must_ be declared explicitly to do nothing.

The body of `set_success()` is:

```cpp
void delete_signaler::set_success( bool success ) {
    if ( detector_ != nullptr ) {
        assert( !detector_->destructor_called() );
        detector_->status_ = success ?
            delete_detector::DTOR_CALLED_SUCCESS :
            delete_detector::DTOR_CALLED_FAILURE;
    }
}
```

To use a `delete_signaler`, one has to be declared as a data member of `operation` and call `set_success()` upon destruction:

```cpp
class operation {
public:
    // ...
    ~operation() {
        delete_signaler_.set_success( success_ );
    }

private:
    bool success_;
    delete_signaler delete_signaler_;
};
```
> Note that calling `set_success()` is needed only in cases like `operation` where there is a “success” result.  If no such result is needed, but the caller still needs to detect whether the callback has deleted it, then there is no need to call `set_success()`.

All the work of signaling the detector is done in `~delete_signaler()` (more later).

Here’s the declaration for `delete_detector`:

```cpp
class delete_detector {
public:
    explicit delete_detector( delete_signaler &signaler );
    ~delete_detector();

    bool destructor_called() const {
        return status_ != DTOR_NOT_CALLED;
    }

    bool success() const {
        return status_ == DTOR_CALLED_SUCCESS;
    }

private:
    enum status {
        DTOR_NOT_CALLED,
        DTOR_CALLED_VOID,
        DTOR_CALLED_SUCCESS,
        DTOR_CALLED_FAILURE
    };

    delete_signaler &signaler_;
    status status_ = DTOR_NOT_CALLED;

    delete_detector( delete_detector const& ) = delete;
    delete_detector& operator=( delete_detector const& ) = delete;

    friend class delete_signaler;
};
```

A `delete_detector` would be used like:

```cpp
bool operation::perform( done_callback *cb ) {
    // ...
    if ( cb != nullptr ) {
        delete_detector detector{ delete_signaler_ };
        cb->operation_done( this );
        if ( detector.destructor_called() )
            return detector.success();
    }
    return success_;                // this->success_ is safe to access
}
```

The constructor for `delete_detector` is fairly straightforward in that it simply sets the signaler’s `detector_` to `this`:

```cpp
delete_detector::delete_detector( delete_signaler &signaler ) :
    signaler_{ signaler }
{
    assert( signaler_.detector_ == nullptr );
    signaler_.detector_ = this;
}
```
The destructor for `delete_detector` not surprisingly does the reverse by setting the signaler’s `detector` back to `nullptr` — but only if the signaler’s destructor was _not_ called:

```cpp
delete_detector::~delete_detector() {
    if ( !destructor_called() ) {
        assert( signaler_.detector_ == this );
        signaler_.detector_ = nullptr;
    }
}
```
One last bit of code is the destructor for `delete_signaler` that sets the detector’s `status_` to `DTOR_CALLED_VOID` but only if it’s `DTOR_NOT_CALLED`, i.e., `set_success()` was _not_ called:

```cpp
delete_signaler::~delete_signaler() {
    if ( !detector->destructor_called() )
        detector->status_ = delete_detector::DTOR_CALLED_VOID;
}
```