/* Copyright (C) 2012 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1 of
   the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the c++-gtk-utils
   sub-directory); if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

However, it is not intended that the object code of a program whose
source code instantiates a template from this file or uses macros or
inline functions (of any length) should by reason only of that
instantiation or use be subject to the restrictions of use in the GNU
Lesser General Public License.  With that in mind, the words "and
macros, inline functions and instantiations of templates (of any
length)" shall be treated as substituted for the words "and small
macros and small inline functions (ten lines or less in length)" in
the fourth paragraph of section 5 of that licence.  This does not
affect any other reason why object code may be subject to the
restrictions in that licence (nor for the avoidance of doubt does it
affect the application of section 2 of that licence to modifications
of the source code in this file).

*/

#ifndef CGU_TASK_MANAGER_TPP
#define CGU_TASK_MANAGER_TPP

namespace Cgu {

namespace Thread {

namespace TaskManagerHelper {

// gcc-4.4 does not support lambdas, and gcc < 4.7 doesn't support
// local structs as template paramaters (say, for
// Callback::make_ref()) even though this is now allowed in C++11:
// unfortunately therefore we now have to have some implementation
// detail in the TaskManagerHelper namespace rather than in the scope
// of the function it is helping.  This is fixed in gcc-4.7 but this
// library targets gcc >= 4.4

template <class Ret, class Func>
struct MemfunResultWrapperArgs {
  SharedLockPtr<AsyncResult<Ret>> ret;
  Func func;
};

// We pass Func as a template type so we do not have to duplicate code
// for const and non-const object methods (this is instead represented
// by the Func type)
template <class Ret, class T, class Func, class... Params>
struct MemfunResultWrapper {
  static void exec(const MemfunResultWrapperArgs<Ret, Func>& wrapper_args,
                   T* obj,
                   const Params&... params) {
    wrapper_args.ret->set((obj->*wrapper_args.func)(params...));
  }
  static void do_fail(const SharedLockPtr<AsyncResult<Ret>>& ret) {
    ret->set_error(); // won't throw
  }
};

// We pass Func as a template type so we do not have to duplicate code
// for const and non-const object methods (this is instead represented
// by the Func type).  The 'when' data member is marked mutable so
// that it can be mutated when the callback is executed.  Normally
// this would be unsafe: however in this particular use it is fine as
// the callback is only ever executed once, via a TaskManager task.
template <class Ret, class Func>
struct MemfunWhenWrapperArgs {
  mutable std::unique_ptr<const Callback::CallbackArg<const Ret&>> when;
  const Callback::Callback* do_fail_cb;
  gint priority;
  GMainContext* context;
  Func func;
  // TODO: these constructors are a work-around for a bug in gcc <
  // 4.6.  At any API break where the required version of gcc is
  // increased to gcc-4.6 or higher, remove them.
  MemfunWhenWrapperArgs(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>&& when_,
			const Callback::Callback* do_fail_cb_,
			gint priority_,
			GMainContext* context_,
			Func func_) : when(std::move(when_)),
				      do_fail_cb(do_fail_cb_),
				      priority(priority_),
				      context(context_),
				      func(func_) {}
  MemfunWhenWrapperArgs(MemfunWhenWrapperArgs&& w): when(std::move(w.when)),
						    do_fail_cb(w.do_fail_cb),
						    priority(w.priority),
						    context(w.context),
						    func(w.func) {}
};

// We pass Func as a template type so we do not have to duplicate code
// for const and non-const object methods (this is instead represented
// by the Func type).  The 'when' data member is marked mutable so
// that it can be mutated when the callback is executed.  Normally
// this would be unsafe: however in this particular use it is fine as
// the callback is only ever executed once, via a TaskManager task.
template <class Ret, class Func>
struct MemfunWhenWrapperArgsRel {
  mutable std::unique_ptr<SafeEmitterArg<const Ret&>> when;
  const Callback::Callback* do_fail_cb;
  gint priority;
  GMainContext* context;
  Func func;
  MemfunWhenWrapperArgsRel(std::unique_ptr<SafeEmitterArg<const Ret&>>&& when_,
			   const Callback::Callback* do_fail_cb_,
			   gint priority_,
			   GMainContext* context_,
			   Func func_) : when(std::move(when_)),
					 do_fail_cb(do_fail_cb_),
					 priority(priority_),
					 context(context_),
					 func(func_) {}
  MemfunWhenWrapperArgsRel(MemfunWhenWrapperArgsRel&& w): when(std::move(w.when)),
							  do_fail_cb(w.do_fail_cb),
							  priority(w.priority),
							  context(w.context),
							  func(w.func) {}
};

// We pass Func as a template type so we do not have to duplicate code
// for const and non-const object methods (this is instead represented
// by the Func type)
template <class Ret, class T, class Func, class... Params>
struct MemfunWhenWrapper {
  static void do_task(const MemfunWhenWrapperArgs<Ret, Func>& wrapper_args,
		      T* obj,
		      const Params&... params) {
    Callback::post(Callback::make_ref(&do_when,
				      std::move(wrapper_args.when),
				      (obj->*wrapper_args.func)(params...)),
		   wrapper_args.priority,
		   wrapper_args.context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete wrapper_args.do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when(const std::unique_ptr<const Callback::CallbackArg<const Ret&>>& when,
		      const Ret& val) {
    when->dispatch(val);
  }
  static void do_task_rel(const MemfunWhenWrapperArgsRel<Ret, Func>& wrapper_args,
			  T* obj,
			  const Params&... params) {
    Callback::post(Callback::make_ref(&do_when_rel,
				      std::move(wrapper_args.when),
				      (obj->*wrapper_args.func)(params...)),
		   wrapper_args.priority,
		   wrapper_args.context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete wrapper_args.do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when_rel(const std::unique_ptr<SafeEmitterArg<const Ret&>>& when_emitter,
			  const Ret& val) {
    when_emitter->emit(val);
  }
  static void post_fail(const Callback::Callback* do_fail_cb,
			gint priority,
			GMainContext* context) {
    // this function does not throw
    Callback::post(do_fail_cb, priority, context);
  }
  static void do_fail(const std::unique_ptr<const Cgu::Callback::Callback>& fail_cb) {
    fail_cb->dispatch();  // won't throw if fail_cb doesn't throw
  }
  static void do_fail_rel(const std::unique_ptr<SafeEmitter>& fail_emitter) {
    fail_emitter->emit(); // might throw std::bad_alloc
  }
};

template <class Ret, class... Params>
struct FunResultWrapperArgs {
  SharedLockPtr<AsyncResult<Ret>> ret;
  Ret (*func)(Params...);
};

template <class Ret, class... Params>
struct FunResultWrapper {
  static void exec(const FunResultWrapperArgs<Ret, Params...>& wrapper_args,
		   const Params&... args) {
    wrapper_args.ret->set(wrapper_args.func(args...));
  }
  static void do_fail(const SharedLockPtr<AsyncResult<Ret>>& ret) {
    ret->set_error(); // won't throw
  }
};

// the 'when' data member is marked mutable so that it can be mutated
// when the callback is executed.  Normally this would be unsafe:
// however in this particular use it is fine as the callback is only
// ever executed once, via a TaskManager task.
template <class Ret, class... Params>
struct FunWhenWrapperArgs {
  typedef Ret (*Func)(Params...);
  mutable std::unique_ptr<const Callback::CallbackArg<const Ret&>> when;
  const Callback::Callback* do_fail_cb;
  gint priority;
  GMainContext* context;
  Func func;
  // TODO: these constructors are a work-around for a bug in gcc <
  // 4.6.  At any API break where the required version of gcc is
  // increased to gcc-4.6 or higher, remove them and the typedef of
  // Func
  FunWhenWrapperArgs(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>&& when_,
		     const Callback::Callback* do_fail_cb_,
		     gint priority_,
		     GMainContext* context_,
		     Func func_) : when(std::move(when_)),
				   do_fail_cb(do_fail_cb_),
				   priority(priority_),
				   context(context_),
				   func(func_) {}
  FunWhenWrapperArgs(FunWhenWrapperArgs&& w): when(std::move(w.when)),
					      do_fail_cb(w.do_fail_cb),
					      priority(w.priority),
					      context(w.context),
					      func(w.func) {}
};

// the 'when' data member is marked mutable so that it can be mutated
// when the callback is executed.  Normally this would be unsafe:
// however in this particular use it is fine as the callback is only
// ever executed once, via a TaskManager task.
template <class Ret, class... Params>
struct FunWhenWrapperArgsRel {
  typedef Ret (*Func)(Params...);
  mutable std::unique_ptr<SafeEmitterArg<const Ret&>> when;
  const Callback::Callback* do_fail_cb;
  gint priority;
  GMainContext* context;
  Func func;
  // TODO: these constructors are a work-around for a bug in gcc <
  // 4.6.  At any API break where the required version of gcc is
  // increased to gcc-4.6 or higher, remove them and the typedef of
  // Func
  FunWhenWrapperArgsRel(std::unique_ptr<SafeEmitterArg<const Ret&>>&& when_,
			const Callback::Callback* do_fail_cb_,
			gint priority_,
			GMainContext* context_,
			Func func_) : when(std::move(when_)),
				      do_fail_cb(do_fail_cb_),
				      priority(priority_),
				      context(context_),
				      func(func_) {}
  FunWhenWrapperArgsRel(FunWhenWrapperArgsRel&& w): when(std::move(w.when)),
						    do_fail_cb(w.do_fail_cb),
						    priority(w.priority),
						    context(w.context),
						    func(w.func) {}
};

template <class Ret, class... Params>
struct FunWhenWrapper {
  static void do_task(const FunWhenWrapperArgs<Ret, Params...>& wrapper_args,
		      const Params&... params) {
    Callback::post(Callback::make_ref(&do_when,
				      std::move(wrapper_args.when),
				      wrapper_args.func(params...)),
		   wrapper_args.priority,
		   wrapper_args.context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete wrapper_args.do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when(const std::unique_ptr<const Callback::CallbackArg<const Ret&>>& when,
		      const Ret& val) {
    when->dispatch(val);
  }
  static void do_task_rel(const FunWhenWrapperArgsRel<Ret, Params...>& wrapper_args,
			  const Params&... params) {
    Callback::post(Callback::make_ref(&do_when_rel,
				      std::move(wrapper_args.when),
				      wrapper_args.func(params...)),
		   wrapper_args.priority,
		   wrapper_args.context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete wrapper_args.do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when_rel(const std::unique_ptr<SafeEmitterArg<const Ret&>>& when_emitter,
			  const Ret& val) {
    when_emitter->emit(val);
  }
  static void post_fail(const Callback::Callback* do_fail_cb,
			gint priority,
			GMainContext* context) {
    // this function does not throw
    Callback::post(do_fail_cb, priority, context);
  }
  static void do_fail(const std::unique_ptr<const Cgu::Callback::Callback>& fail_cb) {
    fail_cb->dispatch();  // won't throw if fail_cb doesn't throw
  }
  static void do_fail_rel(const std::unique_ptr<SafeEmitter>& fail_emitter) {
    fail_emitter->emit(); // might throw std::bad_alloc
  }
};

template <class Ret, class FType>
struct FunctorWhenWrapper {
  static void do_task(FType& f,
		      std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>& when,
		      const Callback::Callback* do_fail_cb,
		      gint priority,
		      GMainContext* context) {
    Callback::post(Callback::make_ref(&do_when,
				      std::move(when),
				      f()),
		   priority,
		   context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when(const std::unique_ptr<const Callback::CallbackArg<const Ret&>>& when,
		      const Ret& val) {
    when->dispatch(val);
  }
  static void do_task_rel(FType& f,
			  std::unique_ptr<SafeEmitterArg<const Ret&>>& when,
			  const Callback::Callback* do_fail_cb,
			  gint priority,
			  GMainContext* context) {
    Callback::post(Callback::make_ref(&do_when_rel,
				      std::move(when),
				      f()),
		   priority,
		   context);
    // if this callback doesn't throw so post_fail() doesn't execute,
    // we need to delete do_fail_cb.  Destructors of bound arguments
    // of do_fail_cb are not supposed to throw, but we might as well
    // deal with it here.  An elipsis catch block is fine as thread
    // cancellation is blocked in all tasks.
    try {
      delete do_fail_cb;
    }
    catch (...) {}
  }
  static void do_when_rel(const std::unique_ptr<SafeEmitterArg<const Ret&>>& when_emitter,
			  const Ret& val) {
    when_emitter->emit(val);
  }
  static void post_fail(const Callback::Callback* do_fail_cb,
			gint priority,
			GMainContext* context) {
    // this function does not throw
    Callback::post(do_fail_cb, priority, context);
  }
  static void do_fail(const std::unique_ptr<const Cgu::Callback::Callback>& fail_cb) {
    fail_cb->dispatch();  // won't throw if fail_cb doesn't throw
  }
  static void do_fail_rel(const std::unique_ptr<SafeEmitter>& fail_emitter) {
    fail_emitter->emit(); // might throw std::bad_alloc
  }
};

/* 
 * The FunctorResultExec class is a specialised class which is
 * necessary because the 'functor' and 'when' members need to be
 * declared mutable so that they can bind to the reference to
 * non-const arguments of FunctorWhenWrapper::do_task() and
 * FunctorWhenWrapper::do_task_rel(), and thus so that a mutable
 * lambda can be executed by that function and a 'when' callback can
 * be passed by std::unique_ptr.  Because it is so specialised, it is
 * not suitable for inclusion in the generic interfaces provided in
 * callback.h.  (Except in this specialised usage, it can also be
 * dangerous, as it allows members of the callback object to be
 * mutated: normally this would be undesirable.)  An alternative would
 * have been to put the 'functor' and 'when' members in a wrapper
 * struct like MemfunWhenWrapperArgs or FunWhenWrapperArgs, but if
 * 'functor' were an lvalue that would mean it being copied twice.
 * This is the most efficient implementation.
 */
template <class FType, class When>
class FunctorWhenExec: public Cgu::Callback::Callback {
public:
  typedef void (*Func)(FType&, When&, const Callback::Callback*, gint, GMainContext*);
private:
  Func func;
  mutable FType functor;
  // 'When' is either a std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>
  //  or a std::unique_ptr<SafeEmitterArg<const Ret&>> type
  mutable When when;
  const Callback::Callback* do_fail_cb;
  gint priority;
  GMainContext* context;
public:
  void dispatch() const {func(functor, when, do_fail_cb, priority, context);}
  template <class FunctorArg>
  FunctorWhenExec(Func func_, FunctorArg&& functor_,
		  When&& when_, const Callback::Callback* do_fail_cb_,
		  gint priority_, GMainContext* context_): func(func_),
							   functor(std::forward<FunctorArg>(functor_)),
							   when(std::move(when_)),
							   do_fail_cb(do_fail_cb_),
							   priority(priority_), context(context_) {}
};

} // namespace TaskManagerHelper

/*** templated task wrappers for functions returning a value ***/

// for non-const non-static member functions

template <class Ret, class... Params, class... Args, class T>
Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> TaskManager::make_task_result(T& t,
									Ret (T::*func)(Params...),
									Args&&... args) {
  static_assert(sizeof...(Args) < 4,
		"No greater than three bound arguments can be passed to "
		"TaskManager::make_task_result() taking a member function.");

  typedef Ret (T::*Func)(Params...);

  SharedLockPtr<AsyncResult<Ret>> ret{new AsyncResult<Ret>};

#ifdef CGU_USE_AUTO_PTR
  typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
  typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

  CbPtr exec_cb(Callback::make_ref(&TaskManagerHelper::MemfunResultWrapper<Ret, T, Func, Params...>::exec,
				   TaskManagerHelper::MemfunResultWrapperArgs<Ret, Func>{ret, func},
				   &t,
				   std::forward<Args>(args)...));
  CbPtr do_fail_cb(Callback::make_ref(&TaskManagerHelper::MemfunResultWrapper<Ret, T, Func, Params...>::do_fail,
				      ret));
  add_task(std::move(exec_cb), std::move(do_fail_cb));

  return ret;
}

template <class Ret, class... Params, class... Args, class T>
#ifdef CGU_USE_AUTO_PTR
void TaskManager::make_task_when_full(std::auto_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::auto_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      T& t,
				      Ret (T::*func)(Params...),
				      Args&&... args)
#else
void TaskManager::make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::unique_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      T& t,
				      Ret (T::*func)(Params...),
				      Args&&... args)
#endif
{
  static_assert(sizeof...(Args) < 4,
		"No greater than three bound arguments can be passed to "
		"TaskManager::make_task_when_full() taking a member function.");

  typedef Ret (T::*Func)(Params...);

  // we construct the callback for MemfunWhenWrapper::do_fail() here
  // rather than in MemfunWhenWrapper::post_fail(), so that
  // MemfunWhenWrapper::post_fail() cannot throw
  Callback::Callback* do_fail_cb = 0;
  if (fail.get()) { // call get() method of fail, as std::auto_ptr is not convertible to bool
    if (fail_releaser) {
      std::unique_ptr<SafeEmitter> fail_emitter(new SafeEmitter);
      fail_emitter->connect(fail.release(), *fail_releaser);
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, T, Func, Params...>::do_fail_rel,
				      std::move(fail_emitter));
    }
    else {
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, T, Func, Params...>::do_fail,
				      // this formula will work for both std::auto_ptr
				      // and std::unique_ptr 'fail' argument
				      std::unique_ptr<const Cgu::Callback::Callback>(fail.release()));
    }
  }

  try {
#ifdef CGU_USE_AUTO_PTR
    typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
    typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

    CbPtr post_fail_cb;
    if (do_fail_cb) {
      post_fail_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, T, Func, Params...>::post_fail,
					    do_fail_cb,
					    priority,
					    context));
    }

    CbPtr do_task_cb;
    if (when_releaser) {
      std::unique_ptr<SafeEmitterArg<const Ret&>> when_emitter(new SafeEmitterArg<const Ret&>);
      when_emitter->connect(when.release(), *when_releaser);
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, T, Func, Params...>::do_task_rel,
					  TaskManagerHelper::MemfunWhenWrapperArgsRel<Ret, Func>{
					    std::move(when_emitter),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  &t,
					  std::forward<Args>(args)...));
    }
    else {
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, T, Func, Params...>::do_task,
					  TaskManagerHelper::MemfunWhenWrapperArgs<Ret, Func>{
					    // this formula will work for both std::auto_ptr and
					    // std::unique_ptr 'when' argument
					    std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>(when.release()),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  &t,
					  std::forward<Args>(args)...));
    }
    add_task(std::move(do_task_cb), std::move(post_fail_cb));
  }
  catch (...) {
    // Destructors of bound arguments of do_fail_cb are not supposed
    // to throw, but we might as well deal with it here.
    CancelBlock b;
    try {
      delete do_fail_cb;
    }
    catch (...) {}
    throw;
  }
}

// for const non-static member functions

template <class Ret, class... Params, class... Args, class T>
Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> TaskManager::make_task_result(const T& t,
									Ret (T::*func)(Params...) const,
									Args&&... args) {
  static_assert(sizeof...(Args) < 4,
		"No greater than three bound arguments can be passed to "
		"TaskManager::make_task_result() taking a member function.");

  typedef Ret (T::*Func)(Params...) const;

  SharedLockPtr<AsyncResult<Ret>> ret{new AsyncResult<Ret>};

#ifdef CGU_USE_AUTO_PTR
  typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
  typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

  CbPtr exec_cb(Callback::make_ref(&TaskManagerHelper::MemfunResultWrapper<Ret, const T, Func, Params...>::exec,
				   TaskManagerHelper::MemfunResultWrapperArgs<Ret, Func>{ret, func},
				   &t,
				   std::forward<Args>(args)...));
  CbPtr do_fail_cb(Callback::make_ref(&TaskManagerHelper::MemfunResultWrapper<Ret, const T, Func, Params...>::do_fail,
				      ret));
  add_task(std::move(exec_cb), std::move(do_fail_cb));

  return ret;
}

template <class Ret, class... Params, class... Args, class T>
#ifdef CGU_USE_AUTO_PTR
void TaskManager::make_task_when_full(std::auto_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::auto_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      const T& t,
				      Ret (T::*func)(Params...) const,
				      Args&&... args)
#else
void TaskManager::make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::unique_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      const T& t,
				      Ret (T::*func)(Params...) const,
				      Args&&... args)
#endif
{
  static_assert(sizeof...(Args) < 4,
		"No greater than three bound arguments can be passed to "
		"TaskManager::make_task_when_full() taking a member function.");

  typedef Ret (T::*Func)(Params...) const;

  // we construct the callback for MemfunWhenWrapper::do_fail() here
  // rather than in MemfunWhenWrapper::post_fail(), so that
  // MemfunWhenWrapper::post_fail() cannot throw
  Callback::Callback* do_fail_cb = 0;
  if (fail.get()) { // call get() method of fail, as std::auto_ptr is not convertible to bool
    if (fail_releaser) {
      std::unique_ptr<SafeEmitter> fail_emitter(new SafeEmitter);
      fail_emitter->connect(fail.release(), *fail_releaser);
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, const T, Func, Params...>::do_fail_rel,
				      std::move(fail_emitter));
    }
    else {
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, const T, Func, Params...>::do_fail,
				      // this formula will work for both std::auto_ptr
				      // and std::unique_ptr 'fail' argument
				      std::unique_ptr<const Cgu::Callback::Callback>(fail.release()));
    }
  }

  try {
#ifdef CGU_USE_AUTO_PTR
    typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
    typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

    CbPtr post_fail_cb;
    if (do_fail_cb) {
      post_fail_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, const T, Func, Params...>::post_fail,
					    do_fail_cb,
					    priority,
					    context));
    }

    CbPtr do_task_cb;
    if (when_releaser) {
      std::unique_ptr<SafeEmitterArg<const Ret&>> when_emitter(new SafeEmitterArg<const Ret&>);
      when_emitter->connect(when.release(), *when_releaser);
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, const T, Func, Params...>::do_task_rel,
					  TaskManagerHelper::MemfunWhenWrapperArgsRel<Ret, Func>{
					    std::move(when_emitter),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  &t,
					  std::forward<Args>(args)...));
    }
    else {
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::MemfunWhenWrapper<Ret, const T, Func, Params...>::do_task,
					  TaskManagerHelper::MemfunWhenWrapperArgs<Ret, Func>{
					    // this formula will work for both std::auto_ptr and
					    // std::unique_ptr 'when' argument
					    std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>(when.release()),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  &t,
					  std::forward<Args>(args)...));
    }
    add_task(std::move(do_task_cb), std::move(post_fail_cb));
  }
  catch (...) {
    // Destructors of bound arguments of do_fail_cb are not supposed
    // to throw, but we might as well deal with it here.
    CancelBlock b;
    try {
      delete do_fail_cb;
    }
    catch (...) {}
    throw;
  }
}

// for static member functions and ordinary functions

template <class Ret, class... Params, class... Args>
Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> TaskManager::make_task_result(Ret (*func)(Params...),
									Args&&... args) {
  static_assert(sizeof...(Args) < 5,
		"No greater than four bound arguments can be passed to "
		"TaskManager::make_task_result() taking a function.");

  SharedLockPtr<AsyncResult<Ret>> ret{new AsyncResult<Ret>};

#ifdef CGU_USE_AUTO_PTR
  typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
  typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

  CbPtr exec_cb(Callback::make_ref(&TaskManagerHelper::FunResultWrapper<Ret, Params...>::exec,
				   TaskManagerHelper::FunResultWrapperArgs<Ret, Params...>{ret, func},
				   std::forward<Args>(args)...));
  CbPtr do_fail_cb(Callback::make_ref(&TaskManagerHelper::FunResultWrapper<Ret, Params...>::do_fail,
				      ret));
  add_task(std::move(exec_cb), std::move(do_fail_cb));

  return ret;
}

template <class Ret, class... Params, class... Args>
#ifdef CGU_USE_AUTO_PTR
void TaskManager::make_task_when_full(std::auto_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::auto_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      Ret (*func)(Params...),
				      Args&&... args)
#else
void TaskManager::make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::unique_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      Ret (*func)(Params...),
				      Args&&... args)
#endif
{
  static_assert(sizeof...(Args) < 5,
		"No greater than four bound arguments can be passed to "
		"TaskManager::make_task_when_full() taking a function.");

  // we construct the callback for FunWhenWrapper::do_fail() here
  // rather than in FunWhenWrapper::post_fail(), so that
  // FunWhenWrapper::post_fail() cannot throw
  Callback::Callback* do_fail_cb = 0;
  if (fail.get()) { // call get() method of fail, as std::auto_ptr is not convertible to bool
    if (fail_releaser) {
      std::unique_ptr<SafeEmitter> fail_emitter(new SafeEmitter);
      fail_emitter->connect(fail.release(), *fail_releaser);
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::FunWhenWrapper<Ret, Params...>::do_fail_rel,
				      std::move(fail_emitter));
    }
    else {
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::FunWhenWrapper<Ret, Params...>::do_fail,
				      // this formula will work for both std::auto_ptr
				      // and std::unique_ptr 'fail' argument
				      std::unique_ptr<const Cgu::Callback::Callback>(fail.release()));
    }
  }

  try {
#ifdef CGU_USE_AUTO_PTR
    typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
    typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

    CbPtr post_fail_cb;
    if (do_fail_cb) {
      post_fail_cb.reset(Callback::make_ref(&TaskManagerHelper::FunWhenWrapper<Ret, Params...>::post_fail,
					    do_fail_cb,
					    priority,
					    context));
    }

    CbPtr do_task_cb;
    if (when_releaser) {
      std::unique_ptr<SafeEmitterArg<const Ret&>> when_emitter(new SafeEmitterArg<const Ret&>);
      when_emitter->connect(when.release(), *when_releaser);
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::FunWhenWrapper<Ret, Params...>::do_task_rel,
					  TaskManagerHelper::FunWhenWrapperArgsRel<Ret, Params...>{
					    std::move(when_emitter),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  std::forward<Args>(args)...));
    }
    else {
      do_task_cb.reset(Callback::make_ref(&TaskManagerHelper::FunWhenWrapper<Ret, Params...>::do_task,
					  TaskManagerHelper::FunWhenWrapperArgs<Ret, Params...>{
					    // this formula will work for both std::auto_ptr and
					    // std::unique_ptr 'when' argument
					    std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>>(when.release()),
					    do_fail_cb,
					    priority,
					    context,
					    func
					  },
					  std::forward<Args>(args)...));
    }
    add_task(std::move(do_task_cb), std::move(post_fail_cb));
  }
  catch (...) {
    // Destructors of bound arguments of do_fail_cb are not supposed
    // to throw, but we might as well deal with it here.
    CancelBlock b;
    try {
      delete do_fail_cb;
    }
    catch (...) {}
    throw;
  }
}

// for callable objects

template <class Ret, class Func>
Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> TaskManager::make_task_result(Func&& f) {

  // there are two types related to the functor to be executed by the
  // task.  'Func' is the transient type provided by argument
  // deduction for forwarding, and will vary depending on whether the
  // functor object is a lvalue (which will deduce it as a reference
  // type) or rvalue (which will not).  'FType' is the type to be held
  // by the callback object generated in this function, and is never a
  // reference type.  It is also never const, because the FType member
  // is marked mutable in the callback object so that it can execute
  // mutable lambdas (or other functors with a non-const operator()()
  // method).
  typedef typename std::remove_const<typename std::remove_reference<Func>::type>::type FType;
#ifdef CGU_USE_AUTO_PTR
  typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
  typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

  SharedLockPtr<AsyncResult<Ret>> ret{new AsyncResult<Ret>};
  CbPtr exec_cb(new TaskManagerHelper::FunctorResultExec<Ret, FType>(std::forward<Func>(f), ret));
  CbPtr do_fail_cb(Callback::make_ref(&TaskManagerHelper::FunctorResultWrapper<Ret, FType>::do_fail,
				      ret));
  add_task(std::move(exec_cb), std::move(do_fail_cb));

  return ret;
}

template <class Ret, class Func>
#ifdef CGU_USE_AUTO_PTR
void TaskManager::make_task_when_full(std::auto_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::auto_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      Func&& f)
#else
void TaskManager::make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
				      Cgu::Releaser* when_releaser,
				      std::unique_ptr<const Cgu::Callback::Callback> fail,
				      Cgu::Releaser* fail_releaser,
				      gint priority,
				      GMainContext* context,
				      Func&& f)
#endif
{

  // there are two types related to the functor to be executed by the
  // task.  'Func' is the transient type provided by argument
  // deduction for forwarding, and will vary depending on whether the
  // functor object is a lvalue (which will deduce it as a reference
  // type) or rvalue (which will not).  'FType' is the type to be held
  // by the callback object generated in this function, and is never a
  // reference type.  It is also never const, because the FType member
  // is marked mutable in the callback object so that it can execute
  // mutable lambdas (or other functors with a non-const operator()()
  // method).
  typedef typename std::remove_const<typename std::remove_reference<Func>::type>::type FType;

  // we construct the callback for FunctorWhenWrapper::do_fail() here
  // rather than in FunctorWhenWrapper::post_fail(), so that
  // FunctorWhenWrapper::post_fail() cannot throw
  Callback::Callback* do_fail_cb = 0;
  if (fail.get()) { // call get() method of fail, as std::auto_ptr is not convertible to bool
    if (fail_releaser) {
      std::unique_ptr<SafeEmitter> fail_emitter(new SafeEmitter);
      fail_emitter->connect(fail.release(), *fail_releaser);
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::FunctorWhenWrapper<Ret, FType>::do_fail_rel,
				      std::move(fail_emitter));
    }
    else {
      do_fail_cb = Callback::make_ref(&TaskManagerHelper::FunctorWhenWrapper<Ret, FType>::do_fail,
				      // this formula will work for both std::auto_ptr
				      // and std::unique_ptr 'fail' argument
				      std::unique_ptr<const Cgu::Callback::Callback>(fail.release()));
    }
  }

  try {
#ifdef CGU_USE_AUTO_PTR
    typedef std::auto_ptr<const Callback::Callback> CbPtr;
#else
    typedef std::unique_ptr<const Callback::Callback> CbPtr;
#endif

    CbPtr post_fail_cb;
    if (do_fail_cb) {
      post_fail_cb.reset(Callback::make_ref(&TaskManagerHelper::FunctorWhenWrapper<Ret, FType>::post_fail,
					    do_fail_cb,
					    priority,
					    context));
    }

    CbPtr do_task_cb;
    if (when_releaser) {
      typedef std::unique_ptr<SafeEmitterArg<const Ret&>> When;
      When when_emitter(new SafeEmitterArg<const Ret&>);
      when_emitter->connect(when.release(), *when_releaser);
      do_task_cb.reset(new TaskManagerHelper::FunctorWhenExec<FType, When>{&TaskManagerHelper::FunctorWhenWrapper<Ret, FType>::do_task_rel,
	                                                                   std::forward<Func>(f),
	                                                                   std::move(when_emitter),
								           do_fail_cb,
								           priority,
	                                                                   context});
    }
    else {
      typedef std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> When;
      do_task_cb.reset(new TaskManagerHelper::FunctorWhenExec<FType, When>{&TaskManagerHelper::FunctorWhenWrapper<Ret, FType>::do_task,
	                                                                   std::forward<Func>(f),
	                                                                   // this formula will work for both std::auto_ptr and
					                                   // std::unique_ptr 'when' argument
	                                                                   When(when.release()),
								           do_fail_cb,
								           priority,
	                                                                   context});
    }
    add_task(std::move(do_task_cb), std::move(post_fail_cb));
  }
  catch (...) {
    // Destructors of bound arguments of do_fail_cb are not supposed
    // to throw, but we might as well deal with it here.
    CancelBlock b;
    try {
      delete do_fail_cb;
    }
    catch (...) {}
    throw;
  }
}

} // namespace Thread

} // namespace Cgu

#endif // CGU_TASK_MANAGER_TPP
