//  (C) Copyright John Maddock 2000. Permission to copy, use, modify, sell and   
//  distribute this software is granted provided this copyright notice appears   
//  in all copies. This software is provided "as is" without express or implied   
//  warranty, and with no claim as to its suitability for any purpose.   

//  common test code for type-traits tests
//  WARNING: contains code as well as declarations!


#ifndef BOOST_TYPE_TRAITS_TEST_HPP
#define BOOST_TYPE_TRAITS_TEST_HPP

#include "boost/config.hpp"
#include "boost/utility.hpp"
#include "boost/type_traits/alignment_of.hpp"
#include "boost/type_traits/type_with_alignment.hpp"
#include "boost/type_traits/ice.hpp"

#include <iostream>
#include <typeinfo>

//
// define tests here
unsigned failures = 0;
unsigned test_count = 0;
//
// This must get defined within the test file.
// All compilers have bugs, set this to the number of
// regressions *expected* from a given compiler,
// if there are no workarounds for the bugs, *and*
// the regressions have been investigated.
//
extern unsigned int expected_failures;
//
// proc check_result()
// Checks that there were no regressions:
//
int check_result(int argc, char** argv)
{
   std::cout << test_count << " tests completed, "
      << failures << " failures found, "
      << expected_failures << " failures expected from this compiler." << std::endl;
   if((argc == 2) 
      && (argv[1][0] == '-')
      && (argv[1][1] == 'a')
      && (argv[1][2] == 0))
   {
      std::cout << "Press any key to continue...";
      std::cin.get();
   }
   return (failures == expected_failures)
       ? 0
       : (failures != 0) ? static_cast<int>(failures) : -1;
}


//
// this one is to verify that a constant is indeed a
// constant-integral-expression:
//
// HP aCC cannot deal with missing names for template value parameters
template <bool b>
struct checker
{
   static void check(bool, bool, const char*, bool){ ++test_count; }
};

template <>
struct checker<false>
{
   static void check(bool o, bool n, const char* name, bool soft)
   {
      ++test_count;
      ++failures;
      // if this is a soft test, then failure is expected,
      // or may depend upon factors outside our control
      // (like compiler options)...
      if(soft)++expected_failures;
      std::cout << "checking value of " << name << "...failed" << std::endl;
      std::cout << "\tfound: " << n << " expected " << o << std::endl;
   }
};

template <class T>
struct typify{};

template <class T, class U>
struct type_checker
{
   static void check(const char* TT, const char*, const char* expression)
   {
      ++test_count;
      if(typeid(typify<T>) != typeid(typify<U>))
      {
         ++failures;
         std::cout << "checking type of " << expression << "...failed" << std::endl;
         std::cout << "   evaluating:  type_checker<" << TT << "," << expression << ">" << std::endl;
         std::cout << "   expected:    type_checker<" << TT << "," << TT << ">" << std::endl;
         std::cout << "   but got:     " << typeid(type_checker<T,U>).name() << std::endl;
      }
   }
};

#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
template <class T>
struct type_checker<T,T>
{
   static void check(const char*, const char*, const char*)
   {
      ++test_count;
   }
};
#endif


#define value_test(v, x) checker<(v == x)>::check(v, x, #x, false);
#define soft_value_test(v, x) checker<(v == x)>::check(v, x, #x, true);

#define value_fail(v, x) \
      ++test_count; \
      ++failures; \
      ++expected_failures;\
      std::cout << "checking value of " << #x << "...failed" << std::endl; \
      std::cout << "   " #x " does not compile on this compiler" << std::endl;


#define type_test(v, x) type_checker<v,x>::check(#v, #x, #x);
#define type_test3(v, x, z) type_checker<v,x,z>::check(#v, #x "," #z, #x "," #z);
#ifndef SHORT_TRANSFORM_TEST
#define transform_check(name, from_suffix, to_suffix)\
   type_test(bool to_suffix, name<bool from_suffix>::type);\
   type_test(char to_suffix, name<char from_suffix>::type);\
   type_test(wchar_t to_suffix, name<wchar_t from_suffix>::type);\
   type_test(signed char to_suffix, name<signed char from_suffix>::type);\
   type_test(unsigned char to_suffix, name<unsigned char from_suffix>::type);\
   type_test(short to_suffix, name<short from_suffix>::type);\
   type_test(unsigned short to_suffix, name<unsigned short from_suffix>::type);\
   type_test(int to_suffix, name<int from_suffix>::type);\
   type_test(unsigned int to_suffix, name<unsigned int from_suffix>::type);\
   type_test(long to_suffix, name<long from_suffix>::type);\
   type_test(unsigned long to_suffix, name<unsigned long from_suffix>::type);\
   type_test(float to_suffix, name<float from_suffix>::type);\
   type_test(long double to_suffix, name<long double from_suffix>::type);\
   type_test(double to_suffix, name<double from_suffix>::type);\
   type_test(UDT to_suffix, name<UDT from_suffix>::type);\
   type_test(enum1 to_suffix, name<enum1 from_suffix>::type);
#else
#define transform_check(name, from_suffix, to_suffix)\
   type_test(int to_suffix, name<int from_suffix>::type);\
   type_test(UDT to_suffix, name<UDT from_suffix>::type);\
   type_test(enum1 to_suffix, name<enum1 from_suffix>::type);
#endif

#define boost_dummy_macro_param

template <class T>
struct test_align
{
   struct padded
   {
      char c;
      T t;
   };

   static void do_it()
   {
      padded p;
      unsigned a = reinterpret_cast<char*>(&(p.t)) - reinterpret_cast<char*>(&p);
      ++test_count;
      // only fail if we do not have a multiple of the actual value:
      if((a > ::boost::alignment_of<T>::value) || (a % ::boost::alignment_of<T>::value))
      {
         ++failures;
         std::cout << "checking value of " << typeid(boost::alignment_of<T>).name() << "...failed" << std::endl;
         std::cout << "\tfound: " << boost::alignment_of<T>::value << " expected " << a << std::endl;
      }
      // suppress warnings about unused variables:
      not_unused(p);
      not_unused(a);
   }
    
   template <class U>
   static void not_unused(U const&) {}
};
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
template <class T>
struct test_align<T&>
{
   static void do_it()
   {
      //
      // we can't do the usual test because we can't take the address
      // of a reference, so check that the result is the same as for a
      // pointer type instead:
      unsigned a = boost::alignment_of<T*>::value;
      ++test_count;
      if(a != boost::alignment_of<T&>::value)
      {
         ++failures;
         std::cout << "checking value of " << typeid(boost::alignment_of<T&>).name() << "...failed" << std::endl;
         std::cout << "\tfound: " << boost::alignment_of<T&>::value << " expected " << a << std::endl;
      }
   }
};
#endif

#define align_test(T) test_align<T>::do_it()

template<class T>
struct test_type_with_align 
{
  typedef typename boost::type_with_alignment<
                     (boost::alignment_of<T>::value)>::type 
    align_t;

  static void do_it()
  {
    int align = boost::alignment_of<T>::value;
    int new_align = boost::alignment_of<align_t>::value;
    ++test_count;
    if (new_align % align != 0) {
      ++failures;
      std::cerr << "checking for an object with same alignment as " 
      << typeid(T).name() << "...failed" << std::endl;
      std::cerr << "\tfound: " << typeid(align_t).name() << std::endl;
    }
  }
};

#define type_with_align_test(T) test_type_with_align<T>::do_it()

//
// the following code allows us to test that a particular
// template functions correctly when instanciated inside another template
// (some bugs only show up in that situation).  For each template
// we declare one NESTED_DECL(classname) that sets up the template class
// and multiple NESTED_TEST(classname, template-arg) declarations, to carry
// the actual tests:
template <bool b>
struct nested_test
{
   typedef nested_test type;
   bool run_time_value;
   const char* what;
   nested_test(bool b2, const char* w) : run_time_value(b2), what(w) { check(); }
   void check()
   {
      ++test_count;
      if(b != run_time_value)
      {
         ++failures;
         std::cerr << "Mismatch between runtime and compile time values in " << what << std::endl;
      }
   }
};

#ifndef __SUNPRO_CC
#define NESTED_DECL(what)\
template <class T> \
struct BOOST_TT_JOIN(nested_tester_,what){\
   nested_test< (::boost::type_traits::ice_ne<0, ::boost::what<T>::value>::value)> tester;\
   BOOST_TT_JOIN(nested_tester_,what)(const char* s) : tester(::boost::what<T>::value, s){}\
};
#define NESTED_TEST(what, with)\
{BOOST_TT_JOIN(nested_tester_,what)<with> check(#what "<" #with ">"); (void)check;}
#else
#define NESTED_DECL(what)
#define NESTED_TEST(what, with)
#endif

#define BOOST_TT_JOIN( X, Y ) BOOST_DO_TT_JOIN( X, Y )
#define BOOST_DO_TT_JOIN( X, Y ) X##Y



//
// define some types to test with:
//
enum enum_UDT{ one, two, three };
struct UDT
{
   UDT(){};
   ~UDT(){};
   UDT(const UDT&);
   UDT& operator=(const UDT&);
   int i;

   void f1();
   int f2();
   int f3(int);
   int f4(int, float);
};

typedef void(*f1)();
typedef int(*f2)(int);
typedef int(*f3)(int, bool);
typedef void (UDT::*mf1)();
typedef int (UDT::*mf2)();
typedef int (UDT::*mf3)(int);
typedef int (UDT::*mf4)(int, float);
typedef int (UDT::*mp);
typedef int (UDT::*cmf)(int) const;

// cv-qualifiers applied to reference types should have no effect
// declare these here for later use with is_reference and remove_reference:
# ifdef BOOST_MSVC
#  pragma warning(push)
#  pragma warning(disable: 4181)
# elif defined(__ICL)
#  pragma warning(push)
#  pragma warning(disable: 21)
# endif
//
// This is intentional:
// r_type and cr_type should be the same type
// but some compilers wrongly apply cv-qualifiers
// to reference types (this may generate a warning
// on some compilers):
//
typedef int& r_type;
typedef const r_type cr_type;
# ifdef BOOST_MSVC
#  pragma warning(pop)
# elif defined(__ICL)
#  pragma warning(pop)
#  pragma warning(disable: 985) // identifier truncated in debug information
# endif

struct POD_UDT { int x; };
struct empty_UDT
{
   ~empty_UDT(){};
   empty_UDT& operator=(const empty_UDT&){ return *this; }
   bool operator==(const empty_UDT&)const
   { return true; }
};
struct empty_POD_UDT
{
   empty_POD_UDT& operator=(const empty_POD_UDT&){ return *this; }
   bool operator==(const empty_POD_UDT&)const
   { return true; }
};
union union_UDT
{
  int x;
  double y;
  ~union_UDT();
};
union POD_union_UDT
{
  int x;
  double y;
};
union empty_union_UDT
{
  ~empty_union_UDT();
};
union empty_POD_union_UDT{};

class Base { };

class Derived : public Base { };

class NonDerived { };

enum enum1
{
   one_,two_
};

enum enum2
{
   three_,four_
};

struct VB
{
   virtual ~VB(){};
};

struct VD : VB
{
   ~VD(){};
};
//
// struct non_pointer:
// used to verify that is_pointer does not return
// true for class types that implement operator void*()
//
struct non_pointer
{
   operator void*(){return this;}
};
struct non_int_pointer
{
   int i;
   operator int*(){return &i;}
};
struct int_constructible
{
   int_constructible(int);
};
struct int_convertible
{
   operator int();
};
//
// struct non_empty:
// used to verify that is_empty does not emit
// spurious warnings or errors.
//
struct non_empty : private boost::noncopyable
{
   int i;
};
//
// abstract base classes:
struct test_abc1
{
   virtual void foo() = 0;
   virtual void foo2() = 0;
};

struct test_abc2
{
   virtual void foo() = 0;
   virtual void foo2() = 0;
};

struct incomplete_type;


#endif // BOOST_TYPE_TRAITS_TEST_HPP
