Esempio di type traits (C++0x)…
Una delle feature che più apprezzo del C++ è il supporto alla template metaprogramming e a tutto ciò che le ruota attorno.
Il prossimo standard C++, C++0x, introduce alcune interessanti novità che possono tornare molto utili in questo ambito come ad esempio i type trait.
Type traits?
Un type trait non è altro che una caratteristica particolare di un tipo. Ad esempio un intero è un tipo primitivo, scalare, non è un oggetto, … Ciascuna di queste caratteristiche è un trait, un “tratto” di un tipo.
Conoscere i “tratti” di un determinato tipo permette di effettuare interessanti operazioni: pensate ad una funzione-template che sulla base del tipo specifico passato in input e dei suoi tratti seleziona l’algoritmo migliore da usare in quel contesto.
In un certo senso l’introduzione nel C++ di questa nuova feature, come al solito derivata dalle Boost C++ Libs (e già inclusa nel cosiddetto Technical Report 1 dello standard C++), permette di superare almeno in parte i limiti della introspection del C++.
Codice d’esempio
Nel codice di esempio che segue mostro una semplice funzione-template che restituisce tutti i tratti di un specifico tipo.
I tratti possono essere divisi in due categorie, “unari” e “binari”: i primi permettono perlopiù di verificare se un tipo ha una certa caratteristica o meno, mentre i secondi permettono di verificare le relazioni fra due tipi (es: se una classe deriva da un’altra).
Ho suddiviso i tratti, recuperati dal draft attuale dello standard in due liste e per espanderle sfrutto un’apposita piccola XMacro.
In una prossima puntata vedremo i trait usati più seriamente… ^^
Nel frattempo, spero che il codice sia comprensibile.
Ciau!
/**
* FILE : TypeAnalyzer.cpp
* AUTHOR : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
* LICENSE : released under the terms of GPL v2.0 ("only")
* COMPILE : g++ -Wall -Winline -pedantic -std=c++0x
* TypeAnalyzer.cpp -o TypeAnalyzer
* PURPOSE : simple C++0x example showing new type traits facility.
*
* TESTED ON :
* - Windows XP, x86 32-bit, G++ 4.4.0
*
* REFERENCES:
* [1]: http://en.wikipedia.org/wiki/C%2B%2B0x#Type_traits_for_metaprogramming
* [2]: http://en.wikipedia.org/wiki/C%2B%2B0x#Rvalue_reference_and_move_semantics
* [3]: http://en.wikipedia.org/wiki/C_preprocessor#X-Macros
* [4]: http://en.wikipedia.org/wiki/C_preprocessor#Quoting_macro_arguments
* [5]: http://en.wikipedia.org/wiki/RTTI
*/
#include "TypeAnalyzer.h"
using namespace std;
// two simple structs...
struct A {};
struct B: A {};
int main(int argc, char **argv)
{
cout << "\nC++0x EXAMPLE:" << endl;
cout << "Simple example showing C++0x type traits facility" << endl;
printTraits<A>();
printTraits<A&>();
printTraits<A&&>(); // rvalue reference, [2]
printTraits<A*>();
printTraits<A, A>();
printTraits<A, B>();
return EXIT_SUCCESS;
}
/**
* FILE : TypeAnalyzer.h
* AUTHOR : Gian Paolo "JP" Ghilardi (http://rejex.wordpress.com)
* LICENSE : released under the terms of GPL v2.0 ("only")
* COMPILE : g++ -Wall -Winline -pedantic -std=c++0x
* TypeAnalyzer.cpp -o TypeAnalyzer
*
* NOTE : for more info, please see file TypeAnalyzer.cpp
*/
#ifndef TYPETRAITS_H_
#define TYPETRAITS_H_
#ifdef __STRICT_ANSI__ // to avoid a MinGW G++ 4.4.0 bug ("swprintf undeclared...")
#undef __STRICT_ANSI__
#include <cstdlib>
#include <iostream>
#define __STRICT_ANSI__
#else
#include <cstdlib>
#include <iostream>
#endif
#include <typeinfo>
#include <type_traits>
using namespace std;
#define TO_STRING(trait) #trait // "Stringizing Operator" [4]
/*
list of C++0x unary type traits
Note #1: not all traits are listed here.
The current C++0x draft (March 2009)
includes more traits. For more info,
please refer to that draft:
"meta.type.synop", $20.6.2.
Note #2: each trait is wrapped to be used
via XMacro [4]
*/
#define UNARY_TRAITS_LIST \
X(is_void); \
X(is_integral); \
X(is_floating_point); \
X(is_array); \
X(is_pointer); \
X(is_lvalue_reference); \
X(is_rvalue_reference); \
X(is_member_object_pointer); \
X(is_member_function_pointer); \
X(is_enum); \
X(is_union); \
X(is_class); \
X(is_function); \
X(is_reference); \
X(is_arithmetic); \
X(is_fundamental); \
X(is_object); \
X(is_scalar); \
X(is_compound); \
X(is_member_pointer); \
X(is_const); \
X(is_volatile); \
X(is_pod); \
X(is_empty); \
X(is_polymorphic); \
X(is_abstract); \
X(has_trivial_default_constructor); \
X(has_trivial_copy_constructor); \
X(has_trivial_assign); \
X(has_trivial_destructor); \
X(has_nothrow_default_constructor); \
X(has_nothrow_copy_constructor); \
X(has_nothrow_assign); \
X(has_virtual_destructor); \
X(is_signed); \
X(is_unsigned);
// Unary traits actually not supported by g++/libstdc++:
// - X(is_trivial);
// - X(is_standard_layout);
/*
list of C++0x binary type traits
*/
#define BINARY_TRAITS_LIST \
X(is_same); \
X(is_base_of); \
X(is_convertible);
// Prints valid unary type traits of a type
template <typename T>
inline void printTraits()
{
cout << "\nTraits of type '" << typeid(T).name() << "':" << endl;
// Define the Xmacro to expand the list of unary type traits [4]
#define X(trait) if(trait<T>::value) \
cout << "- " << TO_STRING(trait) << endl;
UNARY_TRAITS_LIST
#undef X
}
// Prints valid binary type traits of a type compared to another type
template <typename T, typename C>
inline void printTraits()
{
cout << "\nTraits of type '" << typeid(T).name() \
<< "' compared to type '" << typeid(C).name() << "'" << endl;
// Define the Xmacro to expand the list of binary type traits [4]
#define X(trait) if(trait<T, C>::value) \
cout << "- " << TO_STRING(trait) << endl;
BINARY_TRAITS_LIST
#undef X
}
#endif /* TYPETRAITS_H_ */
Il codice in esecuzione
Questo è l’output del programma in esecuzione…


Trovo interessante come sia possibile comparare i trait tra due tipi …
Già, è una cosa stupenda considerato poi che è sempre a disposizione…