/*
 * Copyright (c) 2017-2023 zhllxt
 *
 * author   : zhllxt
 * email    : 37792738@qq.com
 * 
 * Distributed under the Boost Software License, Version 1.0. (See accompanying
 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 *
 * refrenced from https://github.com/loveyacper/ananas
 */

#ifndef __ASIO2_DEFER_HPP__
#define __ASIO2_DEFER_HPP__

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <functional>

namespace asio2
{
	class defer
	{
	protected:
		template<typename F, typename Void, typename... Args>
		struct is_callable : std::false_type {};

		template<typename F, typename... Args>
		struct is_callable<F, std::void_t<decltype(std::declval<std::decay_t<F>&>()
			((std::declval<Args>())...)), char>, Args...> : std::true_type {};

	public:
		defer() noexcept = default;

		// movable
		defer(defer&&) noexcept = default;
		defer& operator=(defer&&) noexcept = default;

		// non copyable
		defer(const defer&) = delete;
		void operator=(const defer&) = delete;

		template <typename Fun, typename... Args,
			std::enable_if_t<is_callable<Fun, void, Args...>::value, int> = 0>
		defer(Fun&& fun, Args&&... args)
		{
			this->fn_ = std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...);
		}

		template <typename Constructor, typename Destructor,
			std::enable_if_t<is_callable<Constructor, void>::value && is_callable<Destructor, void>::value, int> = 0>
		defer(Constructor&& ctor, Destructor&& dtor)
		{
			(ctor)();

			this->fn_ = std::forward<Destructor>(dtor);
		}

		~defer() noexcept
		{
			if (this->fn_) this->fn_();
		}

	protected:
		std::function<void()> fn_;
	};

#ifndef ASIO2_CONCAT
#define ASIO2_CONCAT(a, b) a##b
#endif
#define ASIO2_MAKE_DEFER(line) ::asio2::defer ASIO2_CONCAT(_asio2_defer_, line) = [&]()
#define ASIO2_DEFER ASIO2_MAKE_DEFER(__LINE__)

}

#endif  // !__ASIO2_DEFER_HPP__