/* Copyright (C) 2010 to 2013 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_DO_IF_H
#define CGU_DO_IF_H

/**
 * @file do_if.h
 * @brief This file provides utility functions for conditional compilation.
 *
 * \#include <c++-gtk-utils/do_if.h>
 *
 * The DoIf namespace of the library provides a small subset of
 * template meta-programming techniques likely to be most relevant to
 * GUI programming (if something more substantial is required,
 * something like boost or loki should be used).
 *
 * For conditional compilation, the library provides the
 * Cgu::DoIf::mem_fun() utility functions which will conditionally
 * compile a call to a non-static class member function if, and only
 * if, the target object has that function as a member.  If it does
 * not have the member function as a member, the call will just do
 * nothing rather than generate a compiler error, as it will also if
 * the target object is const and the member function to be called is
 * non-const.  In addition Cgu::DoIf::fun() will conditionally compile
 * a call to an ordinary function (or static member function) if, and
 * only if, the target object is convertible to the type (say, a base
 * type) held by pointer as the first argument of that function.
 *
 * They therefore provide compile-time inheritance checking based on
 * the static type of the object passed to them.  They save dynamic
 * casting (and therefore run time overhead and the requirement for
 * target types to have a virtual method) in templated functions when
 * the static type is all that is necessary to enable type checking to
 * be carried out.
 *
 * This is not just a template curiosity, but enables more generic
 * programming practices when, say, passing mixins to templated
 * functions.  A templated function can be passed an object as a
 * template type, and that function can then conditionally call member
 * functions of objects passed to it.  If the object has a particular
 * function as a member, Cgu::DoIf::mem_fun() will call the function,
 * and if not it will do nothing without causing a compilation error.
 * It can be particularly useful for templated set-up or tear-down
 * functions intended to take a wide variety of different objects with
 * differing features.
 *
 * As mentioned above, Cgu::DoIf::mem_fun() and Cgu::DoIf::fun() will
 * do nothing if an attempt is made to execute a non-const function on
 * a const object, as well as if the object type does not match the
 * function or member function sought to be invoked.  If a compilation
 * failure is wanted in a case of const mismatch instead of doing
 * nothing, from version 2.0.1 the static assertion
 * Cgu::DoIf::assert_not_const() can be invoked before the call to a
 * non-const function is made by Cgu::DoIf::mem_fun() or
 * Cgu::DoIf::fun().
 *
 * Non-static member functions
 * ---------------------------
 *
 * The first argument to be passed to Cgu::DoIf::mem_fun() is the
 * target object to be tested and the second argument is the
 * non-static member function which is to be called with respect to
 * that object if it has the function as a member.  Any number of
 * further arguments can be passed, representing the arguments to be
 * provided to that member function.  These further arguments may be
 * reference arguments (const or non-const), value arguments or
 * r-values, and for automatic pass-through they are passed by r-value
 * reference and std::forward(), so no overhead should be created if
 * value or r-value arguments are passed via Cgu::DoIf::mem_fun().
 *
 * As an example, take a class which uses multiple inheritance to
 * inherit from Cgu::EmitterArg as a mixin (normally a class would
 * have an Emitter object as a member, but it can be inherited from).
 * A container of objects could have the emit() method called on them
 * in a case where they do inherit from EmitterArg, where the code
 * does nothing if they do not.  For example:
 * @code
 *   class Calc {
 *   public:
 *     void do_calc(void);
 *   };
 *
 *   class CalcEmit: public Calc,
 *                   public Cgu::EmitterArg<int> {
 *   };
 *
 *   ... some code blocks elsewhere ...
 *
 *   template <class T>
 *   void dispatch_calcs(const std::vector<T>& v) {
 *     int count;
 *     typename std::vector<T>::const_iterator iter;
 *     for (count = 0, iter = v.begin();
 *          iter != v.end();
 *          ++count, ++iter) {
 *       iter->do_calc();
 *       Cgu::DoIf::mem_fun(*iter,                       // object to be tested
 *                          &Cgu::EmitterArg<int>::emit, // member function to be conditionally called
 *                          count);                      // argument
 *     }
 *   }
 * @endcode
 *
 * Ordinary functions and static member functions
 * ----------------------------------------------
 *
 * The use of Cgu::DoIf::fun() with normal functions and static member
 * functions is similar to the use of mem_fun() with non-static member
 * functions, except that the first argument of the function to be
 * conditionally called must be a pointer to a type to which the
 * target object can prospectively be converted.  The first argument
 * to be passed to Cgu::DoIf::fun() is the target object to be tested
 * and the second argument is the function which is to be
 * conditionally called if the conversion can be accomplished.  If the
 * function to be conditionally called is so called, the target object
 * will be passed by pointer to it.  Any number of further arguments
 * can be passed to Cgu::DoIf::fun(), representing additional
 * arguments to be provided to the function to be conditionally
 * called, which may be reference arguments, value arguments or
 * r-value reference arguments (and automatic pass-through is
 * provided, as in the case of mem_fun()).
 *
 * The use of Cgu::DoIf::fun() with ordinary functions can be useful
 * for batching a number of calls to be made to a test object.  For
 * example:
 *
 * @code
 *   class Calc {
 *   public:
 *     void do_calc();
 *   };
 *
 *   class CalcEmit: public Calc,
 *                   public Cgu::EmitterArg<int> {
 *   public:
 *     void clean_up(int);
 *   };
 *
 *   ... some code blocks elsewhere ...
 *
 *   void emit_on_calcs(CalcEmit* c, int count) {
 *     c->emit(count);
 *     c->clean_up(count);
 *   }
 *
 *   template <class T>
 *   void dispatch_calcs(const std::vector<T>& v) {
 *     int count;
 *     typename std::vector<T>::const_iterator iter;
 *     for (count = 0, iter = v.begin();
 *          iter != v.end();
 *          ++count, ++iter) {
 *       iter->do_calc();
 *       Cgu::DoIf::fun(*iter,          // object to be tested against first argument of function to be conditionally called
 *                      &emit_on_calcs, // function to be conditionally called
 *                      count);         // second argument of function to be conditionally called
 *     }
 *   }
 * @endcode
 *
 * Return values
 * -------------
 *
 * If the function to be called returns something other than void and
 * the function to be conditionally called is not called, then
 * mem_fun() and fun() pass back a dummy object of the return type
 * using that type's default constructor.  The returned object has no
 * meaning beyond that in such a case, so it should not be relied on
 * as holding anything other than its default value on default
 * construction in a case of failure.  This in turn means that any
 * return value, if not a built-in type, must have a default
 * constructor.
 *
 * Overloaded functions
 * --------------------
 *
 * Where a referenced member function or ordinary function is
 * overloaded, this will cause difficulties in template type deduction
 * when mem_fun() or fun() is called, requiring explicit
 * disambiguation.  For example the following will fail to compile
 * unless explicitly disambiguated:
 * @code
 *   class A {
 *   public:
 *     void do_it(int i);
 *     void do_it(int i, int j);
 *     void do_it(double d);
 *   };
 *   int func(A* a, int i);
 *   int func(A* a, double d);
 *
 *   template <class T>
 *   void do_it_if(T& t) {
 *     int i = 1, j = 2;
 *     double d = 10.0;
 *     Cgu::DoIf::mem_fun(t, &A::do_it, i);                                        // won't compile
 *     Cgu::DoIf::mem_fun(t, &A::do_it, i, j);                                     // won't compile
 *     Cgu::DoIf::mem_fun(t, &A::do_it, d);                                        // won't compile
 *     Cgu::DoIf::fun(t, func, i);                                                 // won't compile
 *     Cgu::DoIf::fun(t, func, d);                                                 // won't compile
 *
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int)>(&A::do_it), i);         // OK
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int, int)>(&A::do_it), i, j); // OK
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(double)>(&A::do_it), d);      // OK
 *     Cgu::DoIf::fun(t, static_cast<int (*)(A*, int)>(func), i);                  // OK
 *     Cgu::DoIf::fun(t, static_cast<int (*)(A*, double)>(func), d);               // OK
 *   }
 * @endcode
 *
 * Static assertion
 * ----------------
 *
 * This library also provides the Cgu::DoIf::assert_related_to_type(),
 * Cgu::DoIf::assert_related_types() and Cgu::DoIf::assert_same_type()
 * static type-checking assertions.  If the matters asserted are not
 * true, a compile time error in the line where the functions are
 * instantiated will occur.
 *
 * These static assertion functions can be viewed as doing the
 * opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun().  The
 * mem_fun() and fun() functions will make a conditional call, and do
 * nothing if the test condition is not met without a compile time or
 * run time error, in order to provide compile time polymorphism.  On
 * the other hand, assert_related_to_type(), assert_related_types()
 * and assert_same_type() cause an explicit compilation error where
 * the test condition is not met at an ascertainable point in the code
 * whilst avoiding a slew of incomprehensible error messages from the
 * compiler.  They enable compile time checking of type related
 * invariants.
 *
 * In addition, from version 2.0.1, a Cgu::DoIf::assert_not_const()
 * static assertion is available.
 *
 * The static assertions add nothing to the compiled object code.
 * They either succeed or fail at compile time.
 */

/**
 * @namespace Cgu::DoIf
 * @brief This namespace provides utility functions for conditional compilation.
 *
 * \#include <c++-gtk-utils/do_if.h>
 *
 * The DoIf namespace of the library provides a small subset of
 * template meta-programming techniques likely to be most relevant to
 * GUI programming (if something more substantial is required,
 * something like boost or loki should be used).
 *
 * For conditional compilation, the library provides the
 * Cgu::DoIf::mem_fun() utility functions which will conditionally
 * compile a call to a non-static class member function if, and only
 * if, the target object has that function as a member.  If it does
 * not have the member function as a member, the call will just do
 * nothing rather than generate a compiler error, as it will also if
 * the target object is const and the member function to be called is
 * non-const.  In addition Cgu::DoIf::fun() will conditionally compile
 * a call to an ordinary function (or static member function) if, and
 * only if, the target object is convertible to the type (say, a base
 * type) held by pointer as the first argument of that function.
 *
 * They therefore provide compile-time inheritance checking based on
 * the static type of the object passed to them.  They save dynamic
 * casting (and therefore run time overhead and the requirement for
 * target types to have a virtual method) in templated functions when
 * the static type is all that is necessary to enable type checking to
 * be carried out.
 *
 * This is not just a template curiosity, but enables more generic
 * programming practices when, say, passing mixins to templated
 * functions.  A templated function can be passed an object as a
 * template type, and that function can then conditionally call member
 * functions of objects passed to it.  If the object has a particular
 * function as a member, Cgu::DoIf::mem_fun() will call the function,
 * and if not it will do nothing without causing a compilation error.
 * It can be particularly useful for templated set-up or tear-down
 * functions intended to take a wide variety of different objects with
 * differing features.
 *
 * As mentioned above, Cgu::DoIf::mem_fun() and Cgu::DoIf::fun() will
 * do nothing if an attempt is made to execute a non-const function on
 * a const object, as well as if the object type does not match the
 * function or member function sought to be invoked.  If a compilation
 * failure is wanted in a case of const mismatch instead of doing
 * nothing, from version 2.0.1 the static assertion
 * Cgu::DoIf::assert_not_const() can be invoked before the call to a
 * non-const function is made by Cgu::DoIf::mem_fun() or
 * Cgu::DoIf::fun().
 *
 * Non-static member functions
 * ---------------------------
 *
 * The first argument to be passed to Cgu::DoIf::mem_fun() is the
 * target object to be tested and the second argument is the
 * non-static member function which is to be called with respect to
 * that object if it has the function as a member.  Any number of
 * further arguments can be passed, representing the arguments to be
 * provided to that member function.  These further arguments may be
 * reference arguments (const or non-const), value arguments or
 * r-values, and for automatic pass-through they are passed by r-value
 * reference and std::forward(), so no overhead should be created if
 * value or r-value arguments are passed via Cgu::DoIf::mem_fun().
 *
 * As an example, take a class which uses multiple inheritance to
 * inherit from Cgu::EmitterArg as a mixin (normally a class would
 * have an Emitter object as a member, but it can be inherited from).
 * A container of objects could have the emit() method called on them
 * in a case where they do inherit from EmitterArg, where the code
 * does nothing if they do not.  For example:
 * @code
 *   class Calc {
 *   public:
 *     void do_calc(void);
 *   };
 *
 *   class CalcEmit: public Calc,
 *                   public Cgu::EmitterArg<int> {
 *   };
 *
 *   ... some code blocks elsewhere ...
 *
 *   template <class T>
 *   void dispatch_calcs(const std::vector<T>& v) {
 *     int count;
 *     typename std::vector<T>::const_iterator iter;
 *     for (count = 0, iter = v.begin();
 *          iter != v.end();
 *          ++count, ++iter) {
 *       iter->do_calc();
 *       Cgu::DoIf::mem_fun(*iter,                       // object to be tested
 *                          &Cgu::EmitterArg<int>::emit, // member function to be conditionally called
 *                          count);                      // argument
 *     }
 *   }
 * @endcode
 *
 * Ordinary functions and static member functions
 * ----------------------------------------------
 *
 * The use of Cgu::DoIf::fun() with normal functions and static member
 * functions is similar to the use of mem_fun() with non-static member
 * functions, except that the first argument of the function to be
 * conditionally called must be a pointer to a type to which the
 * target object can prospectively be converted.  The first argument
 * to be passed to Cgu::DoIf::fun() is the target object to be tested
 * and the second argument is the function which is to be
 * conditionally called if the conversion can be accomplished.  If the
 * function to be conditionally called is so called, the target object
 * will be passed by pointer to it.  Any number of further arguments
 * can be passed to Cgu::DoIf::fun(), representing additional
 * arguments to be provided to the function to be conditionally
 * called, which may be reference arguments, value arguments or
 * r-value reference arguments (and automatic pass-through is
 * provided, as in the case of mem_fun()).
 *
 * The use of Cgu::DoIf::fun() with ordinary functions can be useful
 * for batching a number of calls to be made to a test object.  For
 * example:
 *
 * @code
 *   class Calc {
 *   public:
 *     void do_calc();
 *   };
 *
 *   class CalcEmit: public Calc,
 *                   public Cgu::EmitterArg<int> {
 *   public:
 *     void clean_up(int);
 *   };
 *
 *   ... some code blocks elsewhere ...
 *
 *   void emit_on_calcs(CalcEmit* c, int count) {
 *     c->emit(count);
 *     c->clean_up(count);
 *   }
 *
 *   template <class T>
 *   void dispatch_calcs(const std::vector<T>& v) {
 *     int count;
 *     typename std::vector<T>::const_iterator iter;
 *     for (count = 0, iter = v.begin();
 *          iter != v.end();
 *          ++count, ++iter) {
 *       iter->do_calc();
 *       Cgu::DoIf::fun(*iter,          // object to be tested against first argument of function to be conditionally called
 *                      &emit_on_calcs, // function to be conditionally called
 *                      count);         // second argument of function to be conditionally called
 *     }
 *   }
 * @endcode
 *
 * Return values
 * -------------
 *
 * If the function to be called returns something other than void and
 * the function to be conditionally called is not called, then
 * mem_fun() and fun() pass back a dummy object of the return type
 * using that type's default constructor.  The returned object has no
 * meaning beyond that in such a case, so it should not be relied on
 * as holding anything other than its default value on default
 * construction in a case of failure.  This in turn means that any
 * return value, if not a built-in type, must have a default
 * constructor.
 *
 * Overloaded functions
 * --------------------
 *
 * Where a referenced member function or ordinary function is
 * overloaded, this will cause difficulties in template type deduction
 * when mem_fun() or fun() is called, requiring explicit
 * disambiguation.  For example the following will fail to compile
 * unless explicitly disambiguated:
 * @code
 *   class A {
 *   public:
 *     void do_it(int i);
 *     void do_it(int i, int j);
 *     void do_it(double d);
 *   };
 *   int func(A* a, int i);
 *   int func(A* a, double d);
 *
 *   template <class T>
 *   void do_it_if(T& t) {
 *     int i = 1, j = 2;
 *     double d = 10.0;
 *     Cgu::DoIf::mem_fun(t, &A::do_it, i);                                        // won't compile
 *     Cgu::DoIf::mem_fun(t, &A::do_it, i, j);                                     // won't compile
 *     Cgu::DoIf::mem_fun(t, &A::do_it, d);                                        // won't compile
 *     Cgu::DoIf::fun(t, func, i);                                                 // won't compile
 *     Cgu::DoIf::fun(t, func, d);                                                 // won't compile
 *
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int)>(&A::do_it), i);         // OK
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int, int)>(&A::do_it), i, j); // OK
 *     Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(double)>(&A::do_it), d);      // OK
 *     Cgu::DoIf::fun(t, static_cast<int (*)(A*, int)>(func), i);                  // OK
 *     Cgu::DoIf::fun(t, static_cast<int (*)(A*, double)>(func), d);               // OK
 *   }
 * @endcode
 *
 * Static assertion
 * ----------------
 *
 * This library also provides the Cgu::DoIf::assert_related_to_type(),
 * Cgu::DoIf::assert_related_types() and Cgu::DoIf::assert_same_type()
 * static type-checking assertions.  If the matters asserted are not
 * true, a compile time error in the line where the functions are
 * instantiated will occur.
 *
 * These static assertion functions can be viewed as doing the
 * opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun().  The
 * mem_fun() and fun() functions will make a conditional call, and do
 * nothing if the test condition is not met without a compile time or
 * run time error, in order to provide compile time polymorphism.  On
 * the other hand, assert_related_to_type(), assert_related_types()
 * and assert_same_type() cause an explicit compilation error where
 * the test condition is not met at an ascertainable point in the code
 * whilst avoiding a slew of incomprehensible error messages from the
 * compiler.  They enable compile time checking of type related
 * invariants.
 *
 * In addition, from version 2.0.1, a Cgu::DoIf::assert_not_const()
 * static assertion is available.
 *
 * The static assertions add nothing to the compiled object code.
 * They either succeed or fail at compile time.
 */

#include <utility>     // for std::forward
#include <type_traits> // for std::true_type, std::false_type and std::remove_const

#include <c++-gtk-utils/cgu_config.h>

namespace Cgu {

namespace DoIf {

/**
 * @class RelatedTest do_if.h c++-gtk-utils/do_if.h
 * @brief Class for compile time testing of inheritance relationships
 * @sa DoIf::mem_fun DoIf::fun
 *
 * This class provides a compile time test of whether the Obj class
 * template parameter type is related to the Base class template
 * parameter type by public derivation, or they are of the same type:
 * the 'value' public member will be true if they are, otherwise
 * false.  Everything in it comprises a compile time constant so that
 * 'value' can be used as a template parameter for conditional
 * complation, and can also be used with the std::enable_if template.
 *
 * This class tests convertibility, and constness is not discarded in
 * making the test.  Whilst a non-const type passed as the second
 * template parameter will (if the other conditions referred to above
 * are met) be shown as related to a const base type or const same
 * type specified as the first template parameter type, the reverse is
 * not true.  This ensures that the DoIf::mem_fun() and DoIf::fun()
 * conditional compilation functions work correctly.
 */

template<class Base, class Obj>
class RelatedTest {

  static std::true_type test(Base*);
  static std::false_type test(...);

  typedef decltype(test(static_cast<Obj*>(0))) TestObjType;
  typedef decltype(test(static_cast<void*>(0))) TestVoidType;

public:
/**
 * A compile time constant which indicates whether the Obj class
 * template parameter type is related to the Base class template
 * parameter type by public derivation, or they are of the same type.
 * Because it is a compile time constant it can be used as a template
 * parameter and therefore for conditional compilation.
 *
 * This constant specifies convertibility, and constness is not
 * discarded in making the test.  Whilst a non-const type passed as
 * the second template parameter will (if the other conditions
 * referred to above are met) be shown as related to a const base type
 * or const same type specified as the first template parameter type,
 * the reverse is not true.  This ensures that the DoIf::mem_fun() and
 * DoIf::fun() conditional compilation functions work correctly.
 */
  static const bool value = TestObjType::value == true
                            && TestVoidType::value == false;
};

/**
 * This function provides a compile time assertion that the static
 * type of the second argument passed to it is related to (that is,
 * either the same as or publicly derived from) the static type of the
 * first argument.  If it is not, a compile time error will occur
 * (with a message that "Cgu::DoIf::assert_related_to_type() failed",
 * and the line at which this function is reported as instantiated
 * will be the line at which the assertion failed).
 *
 * For example "Cgu::DoIf::assert_related_to_type(a, b)" will fail to
 * compile unless the static type of 'b' is the same as or publicly
 * derived from the static type of 'a'.  In making this test,
 * constness is discarded and not taken into account.
 *
 * See assert_related_types() for a test where the ordering of the
 * arguments is not significant.
 *
 * This function can be viewed as doing the opposite of
 * Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation
 * error where the test is not met, rather than doing nothing.
 * However, unlike those functions, as mentioned above
 * assert_related_to_type() discards constness in making the test.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 */
template <class Base, class Obj>
void assert_related_to_type(const Base& b, const Obj& o) noexcept {
  static_assert(RelatedTest<Base, Obj>::value,
		"Cgu::DoIf::assert_related_to_type() failed");
}

/**
 * This function provides a compile time assertion that the static
 * type of the argument passed to it is related to (that is, either
 * the same as or publicly derived from) the type given as the
 * explicit template parameter to the function call.  If it is not, a
 * compile time error will occur (with a message that
 * "Cgu::DoIf::assert_related_to_type() failed", and the line at which
 * this function is reported as instantiated will be the line at which
 * the assertion failed).
 *
 * For example "Cgu::DoIf::assert_related_to_type<A>(x)" will fail to
 * compile unless the static type of 'x' is the same as or publicly
 * derived from type A.  In making this test, constness is discarded
 * and not taken into account.
 *
 * This function can be viewed as doing the opposite of
 * Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation
 * error where the test is not met, rather than doing nothing.
 * However, unlike those functions, as mentioned above
 * assert_related_to_type() discards constness in making the test.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 */
template <class Base, class Obj>
void assert_related_to_type(const Obj& o) noexcept {
  static_assert(RelatedTest<Base, Obj>::value,
		"Cgu::DoIf::assert_related_to_type() failed");
}

/**
 * This function provides a compile time assertion that the static
 * type of the first argument passed to it is the same as the static
 * type of the second argument.  If it is not, a compile time error
 * will occur (with a message that "Cgu::DoIf::assert_same_type()
 * failed", and the line at which this function is reported as
 * instantiated will be the line at which the assertion failed).
 *
 * For example "Cgu::DoIf::assert_same_type(a, b)" will fail to
 * compile unless the static types of 'a' and 'b' are the same.  In
 * making this test, constness is discarded and not taken into
 * account: types can be the same even if one is const and the other
 * is not.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 */
template <class T1, class T2>
void assert_same_type(const T1& t1, const T2& t2) noexcept {
  static_assert(RelatedTest<T1, T2>::value && RelatedTest<T2, T1>::value,
		"Cgu::DoIf::assert_same_type() failed");
}

/**
 * This function provides a compile time assertion that the static
 * type of the argument passed to it is the same as the type given as
 * the explicit template parameter to the function call.  If it is
 * not, a compile time error will occur (with a message that
 * "Cgu::DoIf::assert_same_type() failed", and the line at which this
 * function is reported as instantiated will be the line at which the
 * assertion failed).
 *
 * For example "Cgu::DoIf::assert_same_type<A>(x)" will fail to
 * compile unless the static type of 'x' is type A.  In making this
 * test, constness is discarded and not taken into account: types can
 * be the same even if one is const and the other is not.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 *
 * Earlier versions of this function had a bug.  A const type could
 * not be specified as the explicit template argument
 * (Cgu::DoIf::assert_same_type<const A>(x) would not work).  This was
 * fixed in version 2.0.1.
 */
template <class T1, class T2>
void assert_same_type(const T2& t2) noexcept {
  // in the second conditional test, make T2 const in case the user
  // specifies a const T1 parameter type
  static_assert(RelatedTest<T1, T2>::value && RelatedTest<const T2, T1>::value,
		"Cgu::DoIf::assert_same_type() failed");
}

/**
 * This function provides a compile time assertion that the static
 * type of the arguments passed are related to each other (that is,
 * either they are the same or one is publicly derived from the
 * other).  If they are not, a compile time error will occur (with a
 * message that "Cgu::DoIf::assert_related_types() failed", and the
 * line at which this function is reported as instantiated will be the
 * line at which the assertion failed).
 *
 * For example "Cgu::DoIf::assert_related_types(a, b)" will fail to
 * compile unless the static types of 'a' and 'b' are the same or one
 * of the static types is publicly derived from the other.  In making
 * this test, constness is discarded and not taken into account: types
 * can be related even if one is const and the other is not.
 *
 * See assert_related_to_type() for a test where the ordering of the
 * arguments is significant, so that which of them must be the base
 * type can be stated.
 *
 * This function can be viewed as doing the opposite of
 * Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation
 * error where the test is not met, rather than doing nothing.
 * However, unlike those functions, as mentioned above
 * assert_related_types() discards constness in making the test.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 */
template <class T1, class T2>
void assert_related_types(const T1& t1, const T2& t2) noexcept {
  static_assert(RelatedTest<T1, T2>::value || RelatedTest<T2, T1>::value,
		"Cgu::DoIf::assert_related_types() failed");
}

/**
 * This function provides a compile time assertion that the static
 * type of the argument passed is not const.  If it is, a compile time
 * error will occur (with a message that
 * "Cgu::DoIf::assert_not_const() failed", and the line at which this
 * function is reported as instantiated will be the line at which the
 * assertion failed).
 *
 * For example 'Cgu::DoIf::assert_not_const(a)' will fail to compile
 * unless the static type of 'a' is not const.
 *
 * It generates no object code, and is therefore thread safe and does
 * not throw.
 *
 * Since 2.0.1
 */
template <class T>
void assert_not_const(T& t) noexcept {
  static_assert(RelatedTest<typename std::remove_const<T>::type, T>::value,
		"Cgu::DoIf::assert_not_const() failed");
}

#ifndef DOXYGEN_PARSING

/*
  These classes and associated helper function employ 'RelatedTest' to
  implement conditional compilation using templates and derivation.
  They can be used with non-static class functions, static class
  functions.
*/
/*
  the static member functions for the 'false' specialisations take
  Args... rather just an elipsis argument so that any argument can be
  a non-POD type
 */

template <bool value, class Ret>
struct cond_call {};

/* cond_call implementation */

/* case of true with a return value */

template <class Ret>
struct cond_call<true, Ret> {
  template <class T, class Func, class... Args>
  static Ret exec(T& obj,
		  Func func,
		  Args&&... args) {
    return (obj.*func)(std::forward<Args>(args)...);
  }

  template <class T, class Func, class... Args>
  static Ret exec_static(T& obj,
			 Func func,
			 Args&&... args) {
    return func(&obj, std::forward<Args>(args)...);
  }
};

/* case of false with a return value */

template <class Ret>
struct cond_call<false, Ret> {
  template <class T, class Func, class... Args>
  static Ret exec(T&,
		  Func,
		  Args&&...) {
    return Ret();
  }

  template <class T, class Func, class... Args>
  static Ret exec_static(T&,
			 Func,
			 Args&&...) {
    return Ret();
  }
};

/* case of true with no return value */

template <>
struct cond_call<true, void> {
  template <class T, class Func, class... Args>
  static void exec(T& obj,
		   Func func,
		   Args&&... args) {
    (obj.*func)(std::forward<Args>(args)...);
  }

  template <class T, class Func, class... Args>
  static void exec_static(T& obj,
			  Func func,
			  Args&&... args) {
    func(&obj, std::forward<Args>(args)...);
  }
};

/* case of false with no return value */

template <>
struct cond_call<false, void> {
  template <class T, class Func, class... Args>
  static void exec(T&,
		   Func,
		   Args&&...) {}

  template <class T, class Func, class... Args>
  static void exec_static(T&,
			  Func,
			  Args&&...) {}
};

#endif /* DOXYGEN_PARSING */

/* the mem_fun() forwarding functions */

/* with return value */

/**
 * This function overload conditionally executes the member function
 * passed as the second argument if the class object passed as the
 * first argument has it as a member (say by derivation), otherwise it
 * does nothing.  This function is thread safe if the referenced
 * member function is thread safe.  It does not throw unless the
 * referenced member function throws or (if its arguments are not
 * built-in types nor reference arguments) the copy constructor of one
 * of the member function's arguments throws, or (if no call is made)
 * the return type's default constructor throws.
 * @return The result of the function call, or if no call is made, an
 * empty object obtained by calling the return type's default
 * constructor.
 */
template <class Base, class Obj, class Ret, class... Params, class... Args>
Ret mem_fun(Obj& obj,
	    Ret (Base::*func)(Params...),
	    Args&&... args) {
  return cond_call<RelatedTest<Base, Obj>::value, Ret>::exec(obj, func,
							     std::forward<Args>(args)...);
}

/* no return value */

/**
 * This function overload conditionally executes the member function
 * passed as the second argument if the class object passed as the
 * first argument has it as a member (say by derivation), otherwise it
 * does nothing.  This function is thread safe if the referenced
 * member function is thread safe.  It does not throw unless the
 * referenced member function throws or (if its arguments are not
 * built-in types nor reference arguments) the copy constructor of one
 * of the member function's arguments throws.
 */
template <class Base, class Obj, class... Params, class... Args>
void mem_fun(Obj& obj,
	     void (Base::*func)(Params...),
	     Args&&... args) {
  cond_call<RelatedTest<Base, Obj>::value, void>::exec(obj, func,
						       std::forward<Args>(args)...);
}

/* for const member functions */

/* with return value */

/**
 * This function overload conditionally executes the member function
 * passed as the second argument if the class object passed as the
 * first argument has it as a member (say by derivation), otherwise it
 * does nothing.  This function is thread safe if the referenced
 * member function is thread safe.  It does not throw unless the
 * referenced member function throws or (if its arguments are not
 * built-in types nor reference arguments) the copy constructor of one
 * of the member function's arguments throws, or (if no call is made)
 * the return type's default constructor throws.
 * @return The result of the function call, or if no call is made, an
 * empty object obtained by calling the return type's default
 * constructor.
 */
template <class Base, class Obj, class Ret, class... Params, class... Args>
Ret mem_fun(const Obj& obj,
	    Ret (Base::*func)(Params...) const,
	    Args&&... args) {
  return cond_call<RelatedTest<Base, Obj>::value, Ret>::exec(obj, func,
							     std::forward<Args>(args)...);
}

/* no return value */

/**
 * This function overload conditionally executes the member function
 * passed as the second argument if the class object passed as the
 * first argument has it as a member (say by derivation), otherwise it
 * does nothing.  This function is thread safe if the referenced
 * member function is thread safe.  It does not throw unless the
 * referenced member function throws or (if its arguments are not
 * built-in types nor reference arguments) the copy constructor of one
 * of the member function's arguments throws.
 */
template <class Base, class Obj, class... Params, class... Args>
void mem_fun(const Obj& obj,
	     void (Base::*func)(Params...) const,
	     Args&&... args) {
  cond_call<RelatedTest<Base, Obj>::value, void>::exec(obj, func,
						       std::forward<Args>(args)...);
}

/* for ordinary functions and static member functions */

/* with return value */
/**
 * This function overload conditionally executes the function passed
 * as the second argument if the object passed as the first argument
 * relates to (ie is an instance of or derived from) the type to which
 * the first argument of the function to be conditionally executed is
 * a pointer, otherwise it does nothing.  This function is thread safe
 * if the referenced function to be called is thread safe.  It does
 * not throw unless the referenced function throws or (if its
 * arguments are not built-in types nor reference arguments) the copy
 * constructor of one of the referenced function's arguments throws,
 * or (if no call is made) the return type's default constructor
 * throws.
 * @return The result of the function call, or if no call is made, an
 * empty object obtained by calling the return type's default
 * constructor.
 */
template <class Base, class Obj, class Ret, class... Params, class... Args>
Ret fun(Obj& obj,
	Ret (*func)(Base*, Params...),
	Args&&... args) {
  return cond_call<RelatedTest<Base, Obj>::value, Ret>::exec_static(obj, func,
								    std::forward<Args>(args)...);
}

/* no return value */

/**
 * This function overload conditionally executes the function passed
 * as the second argument if the object passed as the first argument
 * relates to (ie is an instance of or derived from) the type to which
 * the first argument of the function to be conditionally executed is
 * a pointer, otherwise it does nothing.  This function is thread safe
 * if the referenced function to be called is thread safe.  It does
 * not throw unless the referenced function throws or (if its
 * arguments are not built-in types nor reference arguments) the copy
 * constructor of one of the referenced function's arguments throws.
 */
template <class Base, class Obj, class... Params, class... Args>
void fun(Obj& obj,
	 void (*func)(Base*, Params...),
	 Args&&... args) {
  cond_call<RelatedTest<Base, Obj>::value, void>::exec_static(obj, func,
							      std::forward<Args>(args)...);
}

} // namespace DoIf

} // namespace Cgu

#endif /* CGU_DO_IF */
