/*
 * Autor: Piotr Buczkowski, 192902, N1-ISIII2
 *
 * Temat: boost::type_traits
 *
 * Opis dotyczy biblioteki w wersji 1.38
 *
 * Biblioteka "boost::type_traits" zawiera zbiór specyficznych klas cech,
 * z których każda obejmuje pojedynczą cechę systemu C++.
 * Na przykład czy dany typ jest wskaźnikiem czy referencją?
 * Lub czy typ posiada konstruktor domyślny albo kwalifikator "const"?
 *
 * Klas "type_traits" używamy ich w programowaniu generycznym
 * do określenia właściwości danego typu i optymalizacji
 * odpowiednich dla danego przypadku.
 *
 * Biblioteka "type_traits" zawiera także zbiór klas
 * dokonujących specyficznych transformacji na danym typie.
 * Potrafią na przykład usunąć kwalifikator "const" lub "volatile".
 * Każda klasa dokonująca transformacji definiuje
 * wewnętrzny składnik "type" będący typem transformacji.
 *
 */

/*
 * Szablon "integral_constant" jest w hierarchii dziedziczenia
 * przodkiem klas "true_type" oraz "false_type"
 */
template <class T, T val>
struct integral_constant
{
	typedef integral_constant<T, val> type;
	typedef T value_type;
	static const T value = val;
};

/*
 * Klasy "type_traits" mają wspólną cechę:
 * każda dziedziczy po "true_type" jeśli ma określoną właściwość
 * lub po "false_type" w przeciwnym wypadku.
 * Tak więc:
 * "true_type" jest typem oznaczającym, że klasa posiada daną cechę
 * "false_type" jest typem oznaczającym, że klasa danej cechy nie posiada
 */
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

/*
 * Rozważmy najprostszą klasę w bibliotece: "is_void<T>".
 * Dziedziczy ona po "true_type" tylko jeśli jako typ "T" podamy "void".
 * Mówimy, że klasa szablonowa "is_void" dostarcza pełnej specjalizacji
 * (full-specialization), gdy typ "T" to "void". Jest to dość ważna technika,
 * ale czasem potrzebujemy czegoś pomiędzy rozwiązaniem generycznym
 * a pełną specjalizacją. To dlatego komitet standaryzacyjny zdefiniował
 * częściową specjalizację (partial template-class specialization).
 */
template <typename T>
struct is_void : public false_type{};

template <>
struct is_void<void> : public true_type{};


/*
 * Kolejny przykład to klasa "is_pointer<T>".
 * Głowna wersja obsługuje przypadki gdy "T" nie jest wskaźnikiem,
 * a częściowa specjalizacja w przeciwnym razie.
 */
template <typename T>
struct is_pointer : public false_type{};

template <typename T>
struct is_pointer<T*> : public true_type{};

/*
 * Generalnie obowiązuje zasada, że aby napisać częściową specjalizację
 * należy uprzednio napisać główną wersję.
 */


/*
 * Przyjrzyjmy się przykładowi pokazującemu sposób w jaki klasy cech mogą być użyte.
 * Pokazuje on w jaki sposób klasy cech mogą być użyte
 * do podjęcia decyzji o optymalizacji w czasie kompilacji.
 * Rozważmy algorytm kopiujący z biblioteki standardowej:
 */
template <typename Iter1, typename Iter2>
Iter2 copy(Iter1 first, Iter1 last, Iter2 out);
/*
 * W pewnych okolicznościach kopiowanie może być przeprowadzone najefektywniej
 * z użyciem funkcji "memcpy", ale by tak uczynić spełnione muszą być następujące warunki:
 *		- operator przypisania nie został zdefiniowany przez programistę
 *		- brak składników będących referencjami
 *		- wszystkie klasy bazowe oraz składniki klasy muszą mieć trywialny operator przypisania
 *
 * Biblioteka "type_traits" dostarcza klasę "has_trivial_assignment" taką,
 * że "has_trivial_assignment<T>::type" ma wartość "true" tylko wtedy,
 * gdy typ "T" ma trywialny operator przypisania.
 */
/*
 * Rozważmy następujący przykład:
 */
namespace detail
{
	/*
	 * Szablon funkcji robiącej wolną, ale bezpieczną kopię.
	 */
	template<typename I1, typename I2, bool b>
	I2 do_copy(I1 first, I1 last, I2 out, const integral_constant<bool, b>&)
	{
	   while(first != last)
	   {
		  *out = *first;
		  ++out;
		  ++first;
	   }
	   return out;
	}

	/*
	 * W tej wersji funkcji kopiującej wymagamy,
	 * aby oba iteratory wskazywały na ten sam typ
	 * oraz by ostatni parametr był typu "true_type".
	 */
	template<typename T>
	T* do_copy(const T* first, const T* last, T* out, const true_type&)
	{
	   memcpy(out, first, (last-first)*sizeof(T));
	   return out+(last-first);
	}
}

/*
 * W tej wersji funkcji kopiującej posługujemy się funkcją "do_copy"
 * przekazując jej informację o typie wartości wskazywanych
 * przy użyciu "has_trivial_assign<value_type>".
 * Spowoduje to wybranie wersji funkcji "do_copy" z "memcpy"
 * tylko tam gdzie tego chcemy
 * (czyli jedynie dla typów posiadających trywialny operator przypisania)
 * lub wersji wolniejszej lecz bezpiecznej w przeciwnym przypadku.
 */
template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
   /*
    * Kopiujemy przy użyciu "memcpy" jeżeli "T" ma trywialny operator przypisania
	* oraz jeśli argumentami są wskaźniki.
    */
   typedef typename std::iterator_traits<I1>::value_type value_type;
   return detail::copy_imp(first, last, out, boost::has_trivial_assign<value_type>());
}


/*
 * W następnym przykładzie rozważmy prostą parę dwóch typów:
 */
template <typename T1, typename T2> 
struct pair 
{
	T1 first;
	T2 second;

	/*
	 * Ta prosta para nie może przechowywać referencji,
	 * gdyż konstruktor musiałby przyjmować wtedy referencje do referencji.
	 */
	pair(const T1& nfirst, const T2& nsecond) : first(nfirst), second(nsecond) {}
};
/*
 * Aby zaradzić powyższemu problemowi posłużymy się klasą "add_reference",
 * która dodaje referencję do typu nie-referencyjnego.
 */
#include <boost/type_traits/add_reference.hpp>
template <typename T1, typename T2> 
struct improved_pair
{
	T1 first;
	T2 second;

	improved_pair(boost::add_reference<const T1>::type nfirst,
		boost::add_reference<const T2>::type nsecond) :
			first(nfirst), second(nsecond) {}
};


/*
 * Powyższe przykłady demonstrowały ideę klas cech
 * oraz to w jaki sposób mogą one zostać użyte.
 * Kompletnej listy dostępnych klas należy szukać w dokumentacji "boost".
 */
