// sgpemv2/templates/down_cast.tcc - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// SGPEMv2 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SGPEMv2. If not, see http://www.gnu.org/licenses/.



#ifndef DOWN_CAST_TCC
#define DOWN_CAST_TCC 1

#if !defined(NDEBUG) && !defined(_MSC_VER)
#include <sys/types.h>
#include <typeinfo>
#include <iostream>
#include <typeinfo>
#include <signal.h>
#include <unistd.h>
#endif

namespace sgpem
{
  /** \brief A debug functor that wraps C++ dynamic_cast
   *
   * Since we may want to debug exceptions easily before
   * the stack becomes unwinded, this functor wraps
   * the normal C++ dynamic_cast<> for references and:
   *
   *   - if the macro NDEBUG isn't defined, and the 
   *   dynamic_cast<> throws a std::bad_cast exception,
   *   suspend the process so that the debugger can 
   *   immediately get a backtrace;
   *   - else, use a static_cast. This is meant for
   *   release code that has been \strong extensively
   *   tested.
   *
   * This template comes in two partial specializations: 
   * one for references, and one for pointers.
   * 
   * Note that you shouldn't use \strong always this 
   * template for all your downcast, but only for those
   * you don't want to test their return value because
   * you assume it's correct.
   */
  template<typename To>
  struct _down_cast
  {
    // Uninstantiable for normal objects. 
    // Partial specializations for references and pointers
    // will follow.

    // We use a functor, and not a normal template function, 
    // because in this way the parameter of operator() 
    // is automatically deduced by the compiler (else we 
    // would have to provide two template parameters), and
    // we can partially instantiate the template with
    // just pointers or references (whereas, you cannot
    // partially instantiate a template of a function).
  };

  // --------------------------------------
  // Specialization for references:

  template<typename To>
  struct _down_cast<To&>
  {
    template<typename From>
    inline To& operator()(From& obj) const
    {
#ifndef NDEBUG
  #ifndef _MSC_VER
      try
        {
          return dynamic_cast<To&>(obj);
        }
      catch(const std::bad_cast& e)
        {
          pid_t me = getpid();
          std::clog << "DEBUG: std::bad_cast exception. "
                    << "Guru Meditation #81310005.48454C50." << std::endl
                    <<  " ** " << e.what() << std::endl
                    << "Process [" << me << "] stopped." << std::endl;
          kill(me, SIGSTOP);
          throw;
        }
  #else
      // For MSVC++ on Win32
      return dynamic_cast<To&>(obj);
  #endif
#else
      return dynamic_cast<To&>(obj);
#endif
    }
  };

  // --------------------------------------
  // Specialization for pointers:

  template<typename To>
  struct _down_cast<To*>
  {
    template<typename From>
    inline To* operator()(From* obj) const
    {
#if !defined(NDEBUG) && !defined(_MSC_VER)
      To* check_ptr = dynamic_cast<To*>(obj);
      if(check_ptr == nullptr)
        {
          pid_t me = getpid();
          std::clog << "DEBUG: dynamic_cast<> returned null pointer. "
                    << "Guru Meditation #81310005.48454C50." << std::endl
                    << "Process [" << me << "] stopped." << std::endl;
          kill(me, SIGSTOP);
          throw;
        }
      return check_ptr;
#else
      return dynamic_cast<To*>(obj);
#endif   
    }
  };

  /** \brief Helper function to check a downcast for references
   *
   * See ::_down_cast for a more extensive documentation. This
   * is just a wrapper to provide a more natural interface to 
   * the user
   */
  template<typename To, typename From>
  inline To down_cast(From& obj)
  {
    return _down_cast<To>()(obj);
  }

  /** \brief Helper function to check a downcast for pointers
   *
   * See ::_down_cast for a more extensive documentation. This
   * is just a wrapper to provide a more natural interface to 
   * the user
   */
  template<typename To, typename From>
  inline To down_cast(From* const obj)
  {
    return _down_cast<To>()(obj);
  }


} //~ namespace sgpem

#endif //~ DOWN_CAST_TCC

