#include <string>
#include <list>
#include <iostream>
#include <stdexcept>

#include <memory>

using namespace std;

class Connection {
    std::string name;
public:
    const std::string& getName() const { return name; }

    explicit Connection(const std::string& n):name(n) {
        std::cout << "Connection " << name << std::endl;
    }

    ~Connection() {
        name = '~' + name;
        std::cout << "~Connection " << name << std::endl;
    }
};

typedef shared_ptr<Connection> ptr_t;

class ConnectionPool {
    std::list<ptr_t> connections;

    // Этот класс предназначен для демонстрации первого варианта создания deleter (get1)
    class ConnectionReleaser {
        std::list<ptr_t>& whereToReturn;
        ptr_t connectionToRelease;
    public:
        ConnectionReleaser(std::list<ptr_t>& lst, const ptr_t& x):whereToReturn(lst), connectionToRelease(x) {}
        ~ConnectionReleaser()
        {
        	cout << "~ConnectionReleaser()" << endl;
        	cout << connectionToRelease.get() << endl;
        	if (0 == connectionToRelease.get())
        	{
        		cout << "NULL" << endl;
        	}
        	else
        	{
        		cout << connectionToRelease->getName() << endl;
        	}
        }

        void operator()(Connection*) {
            whereToReturn.push_back( connectionToRelease );
            std::cout << "get1: Returned connection " << connectionToRelease->getName() << " to the list" << std::endl;

            // Закомментируйте след. строку и обратите внимание на разницу в выходной печати
            //connectionToRelease.reset();
        }
    };

    // Эта функция предназначена для демонстрации второго варианта создания deleter (get2)
    static void releaseConnection(std::list<ptr_t>& whereToReturn, ptr_t& connectionToRelease) {
        whereToReturn.push_back( connectionToRelease );
        std::cout << "get2: Returned connection " << connectionToRelease->getName() << " to the list" << std::endl;

        // Закомментируйте следующую строку и обратите внимание на разницу в выходной печати
        connectionToRelease.reset();
    }

    ptr_t popConnection() {
        if( connections.empty() ) throw std::runtime_error("No connections left");
        ptr_t w( connections.back() );
        connections.pop_back();
        return w;
    }
public:
    ptr_t get1() {
        ptr_t w = popConnection();
        std::cout << "get1: Taken connection " << w->getName() << " from list" << std::endl;
        ptr_t r( w.get(), ConnectionReleaser( connections, w ) );
        return r;
    }

    ptr_t get2() {
        ptr_t w = popConnection();
        std::cout << "get2: Taken connection " << w->getName() << " from list" << std::endl;
        ptr_t r( w.get(), bind(&releaseConnection, ref(connections), w ));
        return r;
    }

    void add(const std::string& name) {
        connections.push_back( ptr_t(new Connection(name)) );
    }

    ConnectionPool() {
        std::cout << "ConnectionPool" << std::endl;
    }

    ~ConnectionPool() {
        std::cout << "~ConnectionPool" << std::endl;
    }
};

int main() {
    weak_ptr<Connection> weak1;
    weak_ptr<Connection> weak2;
    {
        ConnectionPool cp;
        cp.add("One");
        cp.add("Two");

        ptr_t p1 = cp.get1();
        weak1 = p1;
        //ptr_t p2 = cp.get2();
        //weak2 = p2;
    }
    std::cout << "weak1.expired()" << weak1.expired() << endl;
    std::cout << "Here the ConnectionPool is out of scope, but weak_ptrs are not" << std::endl;
    return 0;
}