//
// future
// ~~~~~~
// Extends the standard future header with the special value use_future.
//
// Copyright (c) 2014 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
//

#ifndef EXECUTORS_EXPERIMENTAL_FUTURE_HEADER
#define EXECUTORS_EXPERIMENTAL_FUTURE_HEADER

#include <experimental/type_traits>
#include <future>
#include <new>

namespace std {
namespace experimental {
inline namespace concurrency_v1 {

// The class template use_future_t defines a set of completion token types for
// use with asynchronous operations.

template <class _Alloc = allocator<void>>
class use_future_t
{
public:
  typedef _Alloc allocator_type;

#if defined(_MSC_VER)
  use_future_t() noexcept
  {
  }
#else
  constexpr use_future_t() noexcept
  {
  }
#endif

  explicit use_future_t(const _Alloc& __a) noexcept
    : _M_allocator(__a)
  {
  }

  template <class _OtherAllocator>
  use_future_t<_OtherAllocator> operator[](
    const _OtherAllocator& __a) const noexcept
  {
    return use_future_t<_OtherAllocator>(__a);
  }

  allocator_type get_allocator() const noexcept
  {
    return _M_allocator;
  }

private:
  allocator_type _M_allocator;
};

#if defined(_MSC_VER)
__declspec(selectany) use_future_t<> use_future;
#else
constexpr use_future_t<> use_future;
#endif

template <class _Alloc, class _R, class... _Args>
  struct handler_type<use_future_t<_Alloc>, _R(_Args...)>;

// Support for packaged_task<>.

template <class _R, class... _Args>
  class async_result<packaged_task<_R(_Args...)>>;

// The class template packaged_token permits lazy construction of a packaged_task
// from a completion token.

template <class _Func, class _Alloc = allocator<void>>
class packaged_token
{
public:
  typedef _Alloc allocator_type;

  explicit packaged_token(_Func __f)
    : _M_func(std::move(__f))
  {
  }

  packaged_token(_Func __f, const _Alloc& __a)
    : _M_func(std::move(__f)),
      _M_allocator(__a)
  {
  }

  allocator_type get_allocator() const noexcept
  {
    return _M_allocator;
  }

private:
  template <class, class> friend class packaged_handler;
  _Func _M_func;
  _Alloc _M_allocator;
};

template <class _Func, class _Alloc, class _R, class... _Args>
  struct handler_type<packaged_token<_Func, _Alloc>, _R(_Args...)>;

template <class _F, class _Alloc = allocator<void>>
  packaged_token<typename decay<_F>::type, _Alloc> package(
    _F&& __f, const _Alloc& __a = _Alloc());

// A packaged task with an associated allocator.

template <class _Signature, class _Alloc>
class packaged_handler : public packaged_task<_Signature>
{
public:
  typedef _Alloc allocator_type;

  template <class _Func>
  explicit packaged_handler(packaged_token<_Func, _Alloc>&& __p)
    : packaged_task<_Signature>(allocator_arg,
#if defined(__APPLE__) && defined(__clang__)
        typename allocator_traits<_Alloc>::template rebind_alloc<char>(__p._M_allocator),
#else
        __p._M_allocator,
#endif
        std::move(__p._M_func)),
      _M_allocator(__p._M_allocator)
  {
  }

  allocator_type get_allocator() const noexcept
  {
    return _M_allocator;
  }

private:
  _Alloc _M_allocator;
};

template <class _Signature, class _Alloc>
class async_result<packaged_handler<_Signature, _Alloc>>;

} // inline namespace concurrency_v1
} // namespace experimental
} // namespace std

#include <experimental/bits/promise_handler.h>
#include <experimental/bits/packaged_task.h>

#endif
