/* Copyright (C) 2002 The gtkmm Development Team
 *
 * 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 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

_DEFS(glibmm,glib)
_CONFIGINCLUDE(glibmmconfig.h)

_IS_DEPRECATED // This whole file is deprecated.

#m4 _PUSH(SECTION_CC_PRE_INCLUDES)
// Don't let glibmm.h include thread.h. Pretend that it's already included.
// glib.h can then be included with G_DISABLE_DEPRECATED defined, and
// the compiler can react if deprecated glib functions are used.
#define _GLIBMM_THREAD_H
#m4 _POP()

#include <glib.h>
#include <glibmm/error.h>
#include <sigc++/sigc++.h>
#include <string>
#include <cstddef>

namespace Glib
{

/**
 * @deprecated The entire Glib::Threads API is deprecated in favor of the
 * standard C++ concurrency API in C++11 and C++14.
 */
namespace Threads
{
//The GMMPROC_EXTRA_NAMESPACE() macro is a hint to generate_wrap_init.pl to put it in the Threads sub-namespace
_GMMPROC_EXTRA_NAMESPACE(Threads)

/** @defgroup Threads Threads
 * %Thread abstraction; including threads, different mutexes,
 * conditions and thread private data.
 *
 * @deprecated The entire Glib::Threads API is deprecated in favor of the
 * standard C++ concurrency API in C++11 and C++14.
 * @{
 */

/// @deprecated Please use std::lock_guard or std::unique_lock instead.
enum NotLock { NOT_LOCK };

/// @deprecated Please use std::lock_guard or std::unique_lock instead.
enum TryLock { TRY_LOCK };

class GLIBMM_API Mutex;
class GLIBMM_API RecMutex;
class GLIBMM_API RWLock;

/** %Exception class for thread-related errors.
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead.
 */
_WRAP_GERROR(ThreadError, GThreadError, G_THREAD_ERROR, NO_GTYPE, decl_prefix GLIBMM_API)


/** Represents a running thread.
 * An instance of this class can only be obtained with create(), self(),
 * or wrap(GThread*).  It's not possible to delete a Thread object.
 * You must call join() to avoid a memory leak.
 *
 * @note g_thread_exit() is not wrapped, because that function exits a thread
 * without any cleanup.  That's especially dangerous in C++ code, since the
 * destructors of automatic objects won't be invoked.  Instead, you can throw
 * a Threads::Thread::Exit exception, which will be caught by the internal thread
 * entry function.
 *
 * @note The thread entry slot doesn't have the void* return value that a
 * GThreadFunc has.  If you want to return any data from your thread,
 * you can pass an additional output argument to the thread's entry slot.
 *
 * @deprecated Please use std::thread instead.
 */
class GLIBMM_API Thread
{
public:

  Thread(const Thread&) = delete;
  Thread& operator=(const Thread&) = delete;

  class Exit;

  //See http://bugzilla.gnome.org/show_bug.cgi?id=512348 about the sigc::trackable issue.
  // TODO: At the next ABI break, consider changing const sigc::slot<void>& slot
  // to const std::function<void()>& func, if it can be assumed that all supported
  // compilers understand the C++11 template class std::function<>.
  /** Creates a new thread.
   * You can wait for this thread's termination by calling join().
   *
   * The new thread executes the function or method @a slot points to.  You can
   * pass additional arguments using sigc::bind().  If the thread was created
   * successfully, it is returned, otherwise a Threads::ThreadError exception is thrown.
   *
   * Because sigc::trackable is not thread-safe, if the slot represents a
   * non-static class method and is created by sigc::mem_fun(), the class concerned
   * should not derive from sigc::trackable. You can use, say, boost::bind() or,
   * in C++11, std::bind() or a C++11 lambda expression instead of sigc::mem_fun().
   *
   * @param slot A slot to execute in the new thread.
   * @return The new Thread* on success.
   * @throw Glib::Threads::ThreadError
   */
  static Thread* create(const sigc::slot<void>& slot);

  // TODO: At next ABI break, remove the single parameter create
  //       method and default name to std::string()

  /** Creates a new named thread.
   * You can wait for this thread's termination by calling join().
   *
   * The new thread executes the function or method @a slot points to.  You can
   * pass additional arguments using sigc::bind().  If the thread was created
   * successfully, it is returned, otherwise a Threads::ThreadError exception is thrown.
   *
   * Because sigc::trackable is not thread-safe, if the slot represents a
   * non-static class method and is created by sigc::mem_fun(), the class concerned
   * should not derive from sigc::trackable. You can use, say, boost::bind() or,
   * in C++11, std::bind() or a C++11 lambda expression instead of sigc::mem_fun().
   *
   * The @a name can be useful for discriminating threads in a debugger.
   * It is not used for other purposes and does not have to be unique.
   * Some systems restrict the length of @a name to 16 bytes.
   *
   * @param slot A slot to execute in the new thread.
   * @param name A name for the new thread.
   * @return The new Thread* on success.
   * @throw Glib::Threads::ThreadError
   *
   * @newin{2,36}
   */
  static Thread* create(const sigc::slot<void>& slot, const std::string& name);

  /** Returns the Thread* corresponding to the calling thread.
   * @return The current thread.
   */
  static Thread* self();

  /** Waits until the thread finishes.
   * Waits until the thread finishes, i.e. the slot, as given to create(),
   * returns or g_thread_exit() is called by the thread.  (Calling
   * g_thread_exit() in a C++ program should be avoided.)  All resources of
   * the thread including the Glib::Threads::Thread object are released.
   */
  void join();

  /** Gives way to other threads waiting to be scheduled.
   * This function is often used as a method to make busy wait less evil.  But
   * in most cases, you will encounter, there are better methods to do that.
   * So in general you shouldn't use this function.
   */
  static void yield();

  GThread*       gobj();
  const GThread* gobj() const;

private:
  // Glib::Thread can neither be constructed nor deleted.
  Thread();
  void operator delete(void*, std::size_t);
};

/** %Exception class used to exit from a thread.
 * @code
 * throw Glib::Threads::Thread::Exit();
 * @endcode
 * Write this if you want to exit from a thread created by Threads::Thread::create().
 * Of course you must make sure not to catch Threads::Thread::Exit by accident, i.e.
 * when using <tt>catch(...)</tt> somewhere in your code.
 *
 * @deprecated Please use std::thread instead.
 */
class GLIBMM_API Thread::Exit
{};

/** A C++ wrapper for the C object.
 *
 * @param gobject The C instance.
 * @return The C++ wrapper.
 *
 * @relates Glib::Threads::Thread
 *
 * @deprecated Please use std::thread instead.
 */
Thread* wrap(GThread* gobject);

/** Represents a mutex (mutual exclusion).
 * It can be used to protect data against shared access.  Try to use
 * Mutex::Lock instead of calling lock() and unlock() directly&nbsp;--
 * it will make your life much easier.
 *
 * @note Glib::Threads::Mutex is not recursive, i.e. a thread will deadlock, if it
 * already has locked the mutex while calling lock().  Use Glib::Threads::RecMutex
 * instead, if you need recursive mutexes.
 *
 * @deprecated Please use std::mutex instead.
 */
class GLIBMM_API Mutex
{
public:
  class Lock;

  Mutex();

  Mutex(const Mutex&) = delete;
  Mutex& operator=(const Mutex&) = delete;

  ~Mutex();

  /** Locks the mutex.
   * If mutex is already locked by another thread, the current thread will
   * block until mutex is unlocked by the other thread.
   * @see Mutex::Lock
   */
  void lock();

  /** Tries to lock the mutex.
   * If the mutex is already locked by another thread, it immediately returns
   * @c false.  Otherwise it locks the mutex and returns @c true.
   * @return Whether the mutex could be locked.
   * @see Mutex::Lock
   */
  bool trylock();

  /** Unlocks the mutex.
   * If another thread is blocked in a lock() call for this mutex, it will be
   * woken and can lock the mutex itself.
   * @see Mutex::Lock
   */
  void unlock();

  GMutex* gobj() { return &gobject_; }

private:
  GMutex gobject_;
};

/** Utility class for exception-safe mutex locking.
 * @par Usage example:
 * @code
 * {
 *   Glib::Threads::Mutex::Lock lock(mutex); // calls mutex.lock()
 *   do_something();
 * } // the destructor calls mutex.unlock()
 * @endcode
 * As you can see, the compiler takes care of the unlocking.  This is not
 * only exception-safe but also much less error-prone.  You could even
 * <tt>return</tt> while still holding the lock and it will be released
 * properly.
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead.
 */
class GLIBMM_API Mutex::Lock
{
public:
  explicit inline Lock(Mutex& mutex);
  inline Lock(Mutex& mutex, NotLock);
  inline Lock(Mutex& mutex, TryLock);

  Lock(const Mutex::Lock&) = delete;
  Mutex::Lock& operator=(const Mutex::Lock&) = delete;

  inline ~Lock();

  inline void acquire();
  inline bool try_acquire();
  inline void release();
  inline bool locked() const;

private:
  Mutex&  mutex_;
  bool    locked_;
};

/** A C++ wrapper for the C object.
 * Do not use operator delete on the returned pointer. If the caller owns the
 * GMutex object, the caller must destroy it in the same way as if this function
 * had not been called.
 *
 * @param gobject The C instance.
 * @result The GMutex* cast to a Glib::Threads::Mutex*.
 *
 * @relates Glib::Threads::Mutex
 */
Mutex* wrap(GMutex* gobject);

/** This represents a recursive mutex.
 * It is similar to a Mutex with the difference
 * that it is possible to lock a RecMutex multiple times in the same
 * thread without deadlock. When doing so, care has to be taken to
 * unlock the recursive mutex as often as it has been locked.
 *
 * @deprecated Please use std::recursive_mutex instead.
 */
class GLIBMM_API RecMutex
{
public:
  class Lock;

  RecMutex();

  RecMutex(const RecMutex&) = delete;
  RecMutex& operator=(const RecMutex&) = delete;

  ~RecMutex();

  void lock();
  bool trylock();
  void unlock();

  GRecMutex* gobj() { return &gobject_; }

private:
  GRecMutex gobject_;
};

/** Utility class for exception-safe locking of recursive mutexes.
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead.
 */
class GLIBMM_API RecMutex::Lock
{
public:
  explicit inline Lock(RecMutex& mutex);
  inline Lock(RecMutex& mutex, NotLock);
  inline Lock(RecMutex& mutex, TryLock);

  Lock(const RecMutex::Lock&) = delete;
  RecMutex::Lock& operator=(const RecMutex::Lock&) = delete;

  inline ~Lock();

  inline void acquire();
  inline bool try_acquire();
  inline void release();
  inline bool locked() const;

private:
  RecMutex& mutex_;
  bool      locked_;
};

/** A C++ wrapper for the C object.
 * Do not use operator delete on the returned pointer. If the caller owns the
 * GRecMutex object, the caller must destroy it in the same way as if this function
 * had not been called.
 *
 * @param gobject The C instance.
 * @result The GRecMutex* cast to a Glib::Threads::RecMutex*.
 *
 * @relates Glib::Threads::RecMutex
 */
RecMutex* wrap(GRecMutex* gobject);

/** This represents a reader-writer lock.
 * It is similar to a Mutex in that it allows
 * multiple threads to coordinate access to a shared resource.
 *
 * The difference to a mutex is that a reader-writer lock discriminates
 * between read-only ('reader') and full ('writer') access. While only
 * one thread at a time is allowed write access (by holding the 'writer'
 * lock via writer_lock()), multiple threads can gain
 * simultaneous read-only access (by holding the 'reader' lock via
 * reader_lock()).
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead, with std::shared_timed_mutex.
 */
class GLIBMM_API RWLock
{
public:
  class ReaderLock;
  class WriterLock;

  RWLock();

  RWLock(const RWLock&) = delete;
  RWLock& operator=(const RWLock&) = delete;

  ~RWLock();

  void reader_lock();
  bool reader_trylock();
  void reader_unlock();

  void writer_lock();
  bool writer_trylock();
  void writer_unlock();

  GRWLock* gobj() { return &gobject_; }

private:
  GRWLock gobject_;
};

/** Utility class for exception-safe locking of read/write locks.
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead, with std::shared_timed_mutex.
 */
class GLIBMM_API RWLock::ReaderLock
{
public:
  explicit inline ReaderLock(RWLock& rwlock);
  inline ReaderLock(RWLock& rwlock, NotLock);
  inline ReaderLock(RWLock& rwlock, TryLock);

  ReaderLock(const RWLock::ReaderLock&) = delete;
  RWLock::ReaderLock& operator=(const RWLock::ReaderLock&) = delete;

  inline ~ReaderLock();

  inline void acquire();
  inline bool try_acquire();
  inline void release();
  inline bool locked() const;

private:
  RWLock& rwlock_;
  bool    locked_;
};

/** Utility class for exception-safe locking of read/write locks.
 *
 * @deprecated Please use std::lock_guard or std::unique_lock instead, with std::shared_timed_mutex.
 */
class GLIBMM_API RWLock::WriterLock
{
public:
  explicit inline WriterLock(RWLock& rwlock);
  inline WriterLock(RWLock& rwlock, NotLock);
  inline WriterLock(RWLock& rwlock, TryLock);

  WriterLock(const RWLock::WriterLock&) = delete;
  RWLock::WriterLock& operator=(const RWLock::WriterLock&) = delete;

  inline ~WriterLock();

  inline void acquire();
  inline bool try_acquire();
  inline void release();
  inline bool locked() const;

private:
  RWLock& rwlock_;
  bool    locked_;
};

/** An opaque data structure to represent a condition.
 * A @a Cond is an object that threads can block on, if they find a certain
 * condition to be false. If other threads change the state of this condition
 * they can signal the @a Cond, such that the waiting thread is woken up.
 *
 * @deprecated Please use std::condition_variable instead.
 *
 * @par Usage example:
 * @code
 * Glib::Threads::Cond  data_cond;
 * Glib::Threads::Mutex data_mutex;
 * void* current_data = nullptr;
 *
 * void push_data(void* data)
 * {
 *   Glib::Threads::Mutex::Lock lock(data_mutex);
 *
 *   current_data = data;
 *   data_cond.signal();
 * }
 *
 * void* pop_data()
 * {
 *   Glib::Threads::Mutex::Lock lock(data_mutex);
 *
 *   while (!current_data)
 *     data_cond.wait(data_mutex);
 *
 *   void* const data = current_data;
 *   current_data = nullptr;
 *
 *   return data;
 * }
 * @endcode
 */
class GLIBMM_API Cond
{
public:
  Cond();

  Cond(const Cond&) = delete;
  Cond& operator=(const Cond&) = delete;

  ~Cond();

  /** If threads are waiting for this @a Cond, exactly one of them is woken up.
   * It is good practice to hold the same lock as the waiting thread, while calling
   * this method, though not required.
   */
  void signal();

  /** If threads are waiting for this @a Cond, all of them are woken up.
   * It is good practice to hold the same lock as the waiting threads, while calling
   * this method, though not required.
   */
  void broadcast();

  /** Waits until this thread is woken up on this @a Cond.
   * The mutex is unlocked before falling asleep and locked again before resuming.
   *
   * @param mutex A @a Mutex that is currently locked.
   *
   * @note It is important to use the @a wait() and @a wait_until() methods
   * only inside a loop, which checks for the condition to be true as it is not
   * guaranteed that the waiting thread will find it fulfilled, even if the signaling
   * thread left the condition in that state. This is because another thread can have
   * altered the condition, before the waiting thread got the chance to be woken up,
   * even if the condition itself is protected by a @a Mutex.
   */
  void wait(Mutex& mutex);

  /** Waits until this thread is woken up on this @a Cond, but not longer
   * than until the time specified by @a end_time.
   * The mutex is unlocked before falling asleep and locked again before resuming.
   *
   * @par Usage example:
   * Extending the example presented in the documentation of class Cond.
   * @code
   * void* pop_data_timed()
   * {
   *   Glib::Threads::Mutex::Lock lock(data_mutex);
   *
   *   // Wait at most 5 seconds.
   *   const gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
   *   while (!current_data)
   *     if (!data_cond.wait_until(data_mutex, end_time)
   *       return nullptr; // timeout
   *
   *   void* const data = current_data;
   *   current_data = nullptr;
   *
   *   return data;
   * }
   * @endcode
   * The end time is calculated once, before entering the loop, and reused.
   * This is the motivation behind the use of absolute time. If a relative time
   * of 5 seconds were passed directly to the call and a spurious wakeup
   * occurred, the program would have to start over waiting again, which would
   * lead to a total wait time of more than 5 seconds.
   *
   * @param mutex A @a Mutex that is currently locked.
   * @param end_time The monotonic time to wait until, in microseconds.
   *                 See g_get_monotonic_time().
   * @return <tt>true</tt> if the condition variable was signalled (or in the case
   *         of a spurious wakeup), <tt>false</tt> if @a end_time has passed.
   *
   * @note It is important to use the @a wait() and @a wait_until() methods
   * only inside a loop, which checks for the condition to be true as it is not
   * guaranteed that the waiting thread will find it fulfilled, even if the signaling
   * thread left the condition in that state. This is because another thread can have
   * altered the condition, before the waiting thread got the chance to be woken up,
   * even if the condition itself is protected by a @a Mutex.
   */
  bool wait_until(Mutex& mutex, gint64 end_time);

  GCond* gobj() { return &gobject_; }

private:
  GCond gobject_;
};

/** Thread-local data pointer.
 *
 * It is recommended that all instances of this class are statically allocated.
 * The first time an instance is used (get(), set() or replace() is called)
 * glib allocates a scarce OS resource that cannot be deallocated.
 *
 * @deprecated Please use the thread_local keyword instead.
 */
template <class T>
class Private
{
public:
  Private(const Private<T>&) = delete;
  Private<T>& operator=(const Private<T>&) = delete;

  using DestructorFunc = void (*) (void*);

  /** Deletes static_cast<T*>(data)
   */
  static void delete_ptr(void* data);

  /** Constructor.
   *
   * @param destructor_func Function pointer, or <tt>nullptr</tt>. If @a destructor_func is not <tt>nullptr</tt>
   * and the stored data pointer is not <tt>nullptr</tt>, this function is called when replace()
   * is called and when the thread exits.
   */
  explicit inline Private(DestructorFunc destructor_func = &Private<T>::delete_ptr);

  /** Gets the pointer stored in the calling thread.
   *
   * @return If no value has yet been set in this thread, <tt>nullptr</tt> is returned.
   */
  inline T* get();

  /** Sets the pointer in the calling thread without calling <tt>destructor_func()</tt>.
   */
  inline void set(T* data);

  /** Sets the pointer in the calling thread and calls <tt>destructor_func()</tt>.
   * If a function pointer (and not <tt>nullptr</tt>) was specified in the constructor, and
   * the stored data pointer before the call to replace() is not <tt>nullptr</tt>, then
   * <tt>destructor_func()</tt> is called with this old pointer value.
   *
   * @newin{2,32}
   */
  inline void replace(T* data);

  GPrivate* gobj() { return &gobject_; }

private:
  GPrivate gobject_;
};

/** @} group Threads */

/*! A glibmm thread example.
 * @example thread/thread.cc
 */


#ifndef DOXYGEN_SHOULD_SKIP_THIS

/***************************************************************************/
/*  inline implementation                                                  */
/***************************************************************************/

/**** Glib::Threads::Mutex::Lock *******************************************/

inline
Mutex::Lock::Lock(Mutex& mutex)
:
  mutex_  (mutex),
  locked_ (true)
{
  mutex_.lock();
}

inline
Mutex::Lock::Lock(Mutex& mutex, NotLock)
:
  mutex_  (mutex),
  locked_ (false)
{}

inline
Mutex::Lock::Lock(Mutex& mutex, TryLock)
:
  mutex_  (mutex),
  locked_ (mutex.trylock())
{}

inline
Mutex::Lock::~Lock()
{
  if(locked_)
    mutex_.unlock();
}

inline
void Mutex::Lock::acquire()
{
  mutex_.lock();
  locked_ = true;
}

inline
bool Mutex::Lock::try_acquire()
{
  locked_ = mutex_.trylock();
  return locked_;
}

inline
void Mutex::Lock::release()
{
  mutex_.unlock();
  locked_ = false;
}

inline
bool Mutex::Lock::locked() const
{
  return locked_;
}


/**** Glib::Threads::RecMutex::Lock ****************************************/

inline
RecMutex::Lock::Lock(RecMutex& mutex)
:
  mutex_  (mutex),
  locked_ (true)
{
  mutex_.lock();
}

inline
RecMutex::Lock::Lock(RecMutex& mutex, NotLock)
:
  mutex_  (mutex),
  locked_ (false)
{}

inline
RecMutex::Lock::Lock(RecMutex& mutex, TryLock)
:
  mutex_  (mutex),
  locked_ (mutex.trylock())
{}

inline
RecMutex::Lock::~Lock()
{
  if(locked_)
    mutex_.unlock();
}

inline
void RecMutex::Lock::acquire()
{
  mutex_.lock();
  locked_ = true;
}

inline
bool RecMutex::Lock::try_acquire()
{
  locked_ = mutex_.trylock();
  return locked_;
}

inline
void RecMutex::Lock::release()
{
  mutex_.unlock();
  locked_ = false;
}

inline
bool RecMutex::Lock::locked() const
{
  return locked_;
}


/**** Glib::Threads::RWLock::ReaderLock ************************************/

inline
RWLock::ReaderLock::ReaderLock(RWLock& rwlock)
:
  rwlock_ (rwlock),
  locked_ (true)
{
  rwlock_.reader_lock();
}

inline
RWLock::ReaderLock::ReaderLock(RWLock& rwlock, NotLock)
:
  rwlock_ (rwlock),
  locked_ (false)
{}

inline
RWLock::ReaderLock::ReaderLock(RWLock& rwlock, TryLock)
:
  rwlock_ (rwlock),
  locked_ (rwlock.reader_trylock())
{}

inline
RWLock::ReaderLock::~ReaderLock()
{
  if(locked_)
    rwlock_.reader_unlock();
}

inline
void RWLock::ReaderLock::acquire()
{
  rwlock_.reader_lock();
  locked_ = true;
}

inline
bool RWLock::ReaderLock::try_acquire()
{
  locked_ = rwlock_.reader_trylock();
  return locked_;
}

inline
void RWLock::ReaderLock::release()
{
  rwlock_.reader_unlock();
  locked_ = false;
}

inline
bool RWLock::ReaderLock::locked() const
{
  return locked_;
}


/**** Glib::Threads::RWLock::WriterLock ************************************/

inline
RWLock::WriterLock::WriterLock(RWLock& rwlock)
:
  rwlock_ (rwlock),
  locked_ (true)
{
  rwlock_.writer_lock();
}

inline
RWLock::WriterLock::WriterLock(RWLock& rwlock, NotLock)
:
  rwlock_ (rwlock),
  locked_ (false)
{}

inline
RWLock::WriterLock::WriterLock(RWLock& rwlock, TryLock)
:
  rwlock_ (rwlock),
  locked_ (rwlock.writer_trylock())
{}

inline
RWLock::WriterLock::~WriterLock()
{
  if(locked_)
    rwlock_.writer_unlock();
}

inline
void RWLock::WriterLock::acquire()
{
  rwlock_.writer_lock();
  locked_ = true;
}

inline
bool RWLock::WriterLock::try_acquire()
{
  locked_ = rwlock_.writer_trylock();
  return locked_;
}

inline
void RWLock::WriterLock::release()
{
  rwlock_.writer_unlock();
  locked_ = false;
}

inline
bool RWLock::WriterLock::locked() const
{
  return locked_;
}

/**** Glib::Threads::Private<T> ********************************************/

// static
template <class T>
void Private<T>::delete_ptr(void* data)
{
  delete static_cast<T*>(data);
}

template <class T> inline
Private<T>::Private(typename Private<T>::DestructorFunc destructor_func)
{
  // gobject_ = G_PRIVATE_INIT(destructor_func);
  // does not compile with --enable-warnings=fatal.
  // GPrivate is a struct, and G_PRIVATE_INIT is an initializer of type { ... }.
  // G_PRIVATE_INIT can be used only in initializations.
  const GPrivate temp = G_PRIVATE_INIT(destructor_func);
  gobject_ = temp;
}

template <class T> inline
T* Private<T>::get()
{
  return static_cast<T*>(g_private_get(&gobject_));
}

template <class T> inline
void Private<T>::set(T* data)
{
  g_private_set(&gobject_, data);
}

template <class T> inline
void Private<T>::replace(T* data)
{
  g_private_replace(&gobject_, data);
}

#endif /* DOXYGEN_SHOULD_SKIP_THIS */

} //namespace Threads

} // namespace Glib
