#include <cstdio>
#include <cstdlib>
#include <utility>


template <typename T>
struct default_delete {
    void operator ()(T *ptr) {
        delete ptr;
    }
};

template <typename T, typename Deleter = default_delete<T>>
class unique_ptr {
public:
    unique_ptr(T* resource = nullptr, Deleter deleter = Deleter{})
        : resource{resource}, deleter{deleter} {}

    unique_ptr(const unique_ptr&) = delete;
    unique_ptr(unique_ptr&& other) 
        : resource{std::move(other.resource)},
          deleter{std::move(other.deleter)} {}

    unique_ptr& operator=(const unique_ptr&) = delete;
    unique_ptr& operator=(unique_ptr&& other) {
        using std::swap;
        swap(other.resource, resource);
        swap(other.deleter, deleter);
    }

    ~unique_ptr() {
        deleter(resource);
    }

    T* get() { return resource; }
    const T* get() const { return resource; }

private:
    T* resource;
    Deleter deleter;
};

int main() {
    // using default deleter
    unique_ptr<int>  int_ptr(new int{});

    // using std::free to delete dynamically allocated memory
    unique_ptr<int, void(*)(void *)> array_ptr(
        reinterpret_cast<int *>(std::malloc(5 * sizeof(int))),
        std::free);

    // using std::fclose to close an opened file
    // You'll have to try this one localy - unable to open file on a server
    /*
    unique_ptr<FILE, int(*)(FILE *)> file_ptr(
        std::fopen("my_file.txt", "w"),
        std::fclose);
    //*/
    // use int_ptr, array_ptr, file_ptr here

    // these resources get released here, at the end of scope
}
