Skip to main content
Post Made Community Wiki by Jamal
Source Link

Take 2

I think I've incorporated all the comments. If I missed something please remind me below.

I changed the Fan class to use std::unique_ptr too and added some code to the skeleton. But let me mention again that it's just a sekelton for some Resource that needs to be moved into Inproc. It doesn't do anything as is.

I changed NULL to nullptr and set the copy constructor/assignment to delete since copying the resources is not allowed. I also added some comment and added debug output and a main() function so you can run it and see what goes on. The first example would compile but couldn't be run.

I couldn't get std::make_unique to work since the constructor is private.

I changed *__ to *init to avoid the reserved namespace. I can't use plain peer since that would shadow the peer() function. So member variables are * and initializers are *_init across the board.

#include <memory>
#include <iostream>
#include <cassert>

#define START std::cout << __PRETTY_FUNCTION__ << std::endl
#define END   std::cout << __PRETTY_FUNCTION__ << " done" << std::endl

class Fan {
public:
    // Factory with dependency inversion, creates all resource before calling
    // the constructor
    static std::unique_ptr<Fan> create(Fan *peer_init = nullptr) {
        START;
        // some resource allocations left out for simplicity
        // epoll file descriptor
        // read and write ready and blocked queues
        // if they fail create returns nullptr

        // error: 'Fan::Fan(Fan*)' is private
        // std::unique_ptr<Fan> fan = std::make_unique<Fan>(peer_init);
        std::unique_ptr<Fan> fan(new(std::nothrow) Fan(peer_init));
        END;
        return fan;
    }

    ~Fan() {
        START;
        std::cout << " this = " << this << " peer = " << peer_ << std::endl;
        if (peer_) peer(nullptr);
        END;
    }
private:
    // no copying
    Fan(const Fan &) = delete;
    Fan & operator =(const Fan &) = delete;

    // set/reset peer_ pointer
    void peer(Fan *peer_init) {
        START;
        std::cout << " before: this = " << this << " peer = " << peer_ << std::endl;
        if (peer_init) { // finish linking peers
            assert(!peer_); // must not have an observer
            assert(peer_init->peer_ == this); // must already be observed by peer
            peer_ = peer_init;
        } else { // break peers apart
            assert(peer_); // must have an observer
            assert(peer_->peer_ == this); // must be observed by peer
            peer_->peer_ = nullptr;
            peer_ = nullptr;
        }
        std::cout << " after: this = " << this << " peer = " << peer_ << std::endl;
        END;
    }

    // constructor connects to other peer if given
    // other resources left out for simplicity
    Fan(Fan * peer_init /*, resource, resource */) : peer_(peer_init) {
        START;
        if (peer_) {
            assert(!peer_->peer_);
            peer_->peer(this);
        }
        END;
    };

    Fan *peer_; // peer on the other side
};

class Inproc {
public:
    // Factory with dependency inversion, creates all resource before calling
    // the constructor
    static std::unique_ptr<Inproc> create() {
        START;
        // create first Fan of a pair
        if (auto bound = Fan::create()) {
            // create second Fan connected to the first
            if (auto connected = Fan::create(bound.get())) {
                // create Inproc with all resource pre-allocated

                /* error: 'Inproc::Inproc(std::unique_ptr<Fan>,
                                          std::unique_ptr<Fan>)' is private
                std::unique_ptr<Inproc> inproc =
                    std::make_unique<Inproc>(std::move(bound),
                                             std::move(connected));
                */
                std::unique_ptr<Inproc> inproc(
                    new(std::nothrow) Inproc(std::move(bound),
                         std::move(connected)));
                // verify that ownership has moved on
                std::cout << " bound is now: " << bound.get() << std::endl;
                END;
                return inproc;
            }
        }
        END;
        return std::unique_ptr<Inproc>(nullptr);
    }
    ~Inproc() {
        START;
        // nothing to do now anymore
        END;
    }
private:
    // constructor takes over ownership of resources
    Inproc(std::unique_ptr<Fan> bound__, std::unique_ptr<Fan> connected__)
        : bound_(std::move(bound__)), connected_(std::move(connected__)) {
            START;
        // other stuff left out for simplicity
        END;
    }

    // no copying
    Inproc(const Inproc &) = delete;
    Inproc & operator =(const Inproc &) = delete;

    // resources
    std::unique_ptr<Fan> bound_;
    std::unique_ptr<Fan> connected_;
};

// test
int main() {
    std::unique_ptr<Inproc> inproc = Inproc::create();
    std::cout << __PRETTY_FUNCTION__ << ": cleaning up" << std::endl;
    inproc.reset();
}