Traits:
emplate <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
T tmp = *it1;
*it1 = *it2;
*it2 = tmp;
}
template <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
typename ForwardIterator1::value_type tmp = *it1;
*it1 = *it2;
*it2 = tmp;
}
But:
void f(int *p1, int *p2)
{
iter_swap(p1,p2);
}
Pointers have no value_type member.
This should be added in non-intrusive way:
template <class Iterator> struct iterator_traits;
template <class ForwardIterator1, class ForwardIterator2>
void iter_swap( ForwardIterator1 it1, ForwardIterator2 it2)
{
typename terator_traits<ForwardIterator1>::value_type tmp = *it1;
*it1 = *it2;
*it2 = tmp;
}
template <>
struct iterator_traits<T*>
{
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ptrdiff_t difference_type;
typedef random_access_iterator_tag iterator_category;
};
template <class Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
};
Character traits:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class std::basic_string
{
public:
};
template<class Ch> struct char_traits { };
template<> struct char_traits<char>
{
typedef char char_type;
static void assign(char_type&, const char_type&);
typedef int int_type;
static char_type to_char_type(const int_type&);
static int_type to_int_type(const char_type&);
static bool eq_int_type(const int_type&, const int_type&);
static bool eq(const char_type&, const char_type&);
static bool lt(const char_type&, const char_type&);
static char_type* move(char_type* s, const char_type* s2, size_t n);
static char_type* copy(char_type* s, const char_type* s2, size_t n);
static char_type* assign(char_type* s, size_t n, char_type a);
static int compare(const char_type* s, const char_type* s2, size_t n);
static size_t length(const char_type*);
static const char_type* find(const char_type* s, int n, const char_type&);
typedef streamoff off_type;
typedef streampos pos_type;
typedef mbstate_t state_type;
static int_type eof();
static int_type not_eof(const int_type& i);
static state_type get_state(pos_type p);
};
template<> struct char_traits<wchar_t>
{
typedef wchar_t char_type;
typedef wint_t int_type;
typedef wstreamoff off_type;
typedef wstreampos pos_type;
};
Usage:
#ifndef CISTRING_H
#define CISTRING_H
#include <string>
struct ci_char_traits : public std::char_traits<char>
{
static bool eq( char c1, char c2)
{
return toupper(c1) == toupper(c2);
}
static bool lt( char c1, char c2)
{
return toupper(c1) < toupper(c2);
}
static int compare( const char *s1, const char *s2, size_t n)
{
return memicmp( s1, s2, n);
}
static const char *find( const char *s, int n, char ch)
{
while ( n-- > 0 && toupper(*s) != toupper(ch) )
{
++s;
}
return n > 0 ? s : 0;
}
};
typedef std::basic_string<char, ci_char_traits> ci_string;
#endif
Let see traits in a complex environment.
This is an ordinary matrix implementation:
template <class T>
class matrix
{
public:
~matrix();
matrix operator=( const matrix &other);
private:
int x;
int y;
T *v;
void copy( const matrix &other);
};
template <class T>
matrix<T> matrix<T>::operator=( const matrix &other)
{
if ( this != &other )
{
delete [] v;
copy( other);
}
return *this;
}
template <class T>
void matrix<T>::copy( const matrix &other)
{
x = other.x;
y = other.y;
v = new T[x*y];
for ( int i = 0; i < x*y; ++i )
v[i] = other.v[i];
}
For the most cases we use POD types as template parameters.
Therefore we can optimize the copy function for POD types:
template <class T>
void matrix<T>::copy( const matrix &other)
{
std::cerr << "elementary copy in loop" << std::endl;
x = other.x;
y = other.y;
v = new T[x*y];
for ( int i = 0; i < x*y; ++i )
v[i] = other.v[i];
}
template <>
void matrix<long>::copy( const matrix &other)
{
std::cerr << "bitwise copy for long" << std::endl;
x = other.x;
y = other.y;
v = new long[x*y];
memcpy( v, other.v, sizeof(long)*x*y);
}
Unfortunately, the solution causes scattered code.
We can make a more structured solution using traits.
template <typename T>
struct copy_trait
{
static void copy( T* to, const T* from, int n)
{
for( int i = 0; i < n; ++i )
to[i] = from[i];
}
};
template <>
struct copy_trait<long>
{
static void copy( long* to, const long* from, int n)
{
memcpy( to, from, n*sizeof(long));
}
};
template <>
struct copy_trait<double>
{
static void copy( double* to, const double* from, int n)
{
memcpy( to, from, n*sizeof(double));
}
};
template <class T, class Cpy = copy_trait<T> >
class matrix
{
public:
};
template <class T, class Cpy>
void matrix<T,Cpy>::copy( const matrix &other)
{
x = other.x;
y = other.y;
v = new T[x*y];
Cpy::copy( v, other.v, x*y);
}
It is better, because we outfactored the copy implementation from matrix.
But we have to repeat the copy function for all POD types. Lets improve it!
template <typename T>
struct is_pod
{
enum { value = false };
};
template <>
struct is_pod<long>
{
enum { value = true };
};
template <typename T, bool B>
struct copy_trait
{
static void copy( T* to, const T* from, int n)
{
for( int i = 0; i < n; ++i )
to[i] = from[i];
}
};
template <typename T>
struct copy_trait<T, true>
{
static void copy( T* to, const T* from, int n)
{
memcpy( to, from, n*sizeof(T));
}
};
template <class T, class Cpy = copy_trait<T,is_pod<T>::value> >
class matrix
{
};
Using template metaprogramming, we can make an even better solution:
typedef TYPELIST_4(char, signed char, unsigned char, int) Pod_types;
template <typename T>
struct is_pod
{
enum { value = ::Loki::TL::IndexOf<Pod_types,T>::value != -1 };
};
template <typename T, bool B>
struct copy_trait
{
static void copy( T* to, const T* from, int n)
{
for( int i = 0; i < n; ++i )
to[i] = from[i];
}
};
template <typename T>
struct copy_trait<T, true>
{
static void copy( T* to, const T* from, int n)
{
memcpy( to, from, n*sizeof(T));
}
};
template <class T, class Cpy = copy_trait<T,is_pod<T>::value> >
class matrix
{
};