traits classes

Sep 10, 2010 at 2:27pm
Hi,

I am trying to figure out how to generically wrap some similar classes from an external library, possibly using traits classes.

The library exposes two classes, say X and Y, and several related classes, say vec_X, vec_Y, mat_X, mat_Y, where vec_X is a vector of X's, mat_X is a matrix of X's, etc. The interfaces for X and Y are fairly regular, and I would like to write some generic code that can operate on objects of type either X or Y.

Suppose for example I have a function that takes either X or Y as input, and needs to operate internally with vec_X/vec_Y and mat_X/mat_Y. The simple solution is to do something like this:

1
2
3
4
5
6
7
8
template<typename val_t, typename vec_t, typename mat_t>
void my_func(const val_t& input)
{
   vec_t my_vec;
   mat_t my_mat;

   ...
}


To operate on X's I would invoke like this:

 
my_func<X, vec_X, mat_X>(...)


This works, but it becomes quite unreadable with all the type parameters written down every time. (In the example above there is only vec and mat; in my actual situation I have many more to deal with.)

I tried to solve this using traits classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// traits class
template<typename T>
struct my_traits { };

// explicit specializations
template<>
struct my_traits<X>
{
  typedef X      val_t;
  typedef vec_X  vec_t;
  typedef mat_X  mat_t;
};

template<>
struct my_traits<Y>
{
  typedef Y      val_t;
  typedef vec_Y  vec_t;
  typedef mat_Y  mat_t;
};

// function I am interested in
template<typename T>
void my_func(const my_traits<T>::val_t& input)
{
  my_traits<T>::vec_t my_vec;
  my_traits<T>::mat_t my_mat;

  ...
}


But this doesn't work, gcc barfs on it. The problem seems to be that the compiler doesn't know enough about "my_traits<T>" at compilation time to be able to allow a function definition with a parameter of type "my_traits<T>::val_t".

Is there some way to do what I'm trying to do? Or do I have to put up with writing out the template parameters every time?

thanks in advance,

david
Sep 10, 2010 at 2:32pm
You can just do this:

1
2
3
4
5
6
7
template< typename T >
void my_func( const T& input )
{
    my_traits<T>::vec_t my_vec;
    my_traits<T>::mat_t my_mat;
    // ...
}

Sep 10, 2010 at 2:45pm
typename typename.

The compiler doesn't know that templated types are really types unless you tell it (Because they might not be types):

1
2
3
4
5
6
7
8
template<typename T>
void my_func(const typename my_traits<T>::val_t& input)
{
  typename my_traits<T>::vec_t my_vec;
  typename my_traits<T>::mat_t my_mat;

  ...
}


of course the way you're passing 'input' is also ambiguous. Since it's a templated type, the compiler won't be able to automatically determine which function to instantiate when you call it. For example:

1
2
3
4
5
6
X foo;
my_func(foo);  // error, compiler doesn't know that T==X because it can't know T until it knows what my_traits<T>::val_t is
  // and it can't know what val_t is until it knows what T is.

// so you have to give it a hint:
my_func<X>(foo);  // now it's OK 


You can avoid this by just passing T instead of the val_t typedef:

1
2
3
4
5
6
7
8
void my_func(const T& input)
{
//...
}

//...
X foo;
my_func(foo);  // now this isn't a problem, as it can easily figure out that T is X 



EDIT: doh, I was too slow. jsmith beat me to it =P
Last edited on Sep 10, 2010 at 2:45pm
Sep 10, 2010 at 3:02pm
[bangs head on wall]

thanks everyone, that's brilliant. The magic typename keyword.

david
Topic archived. No new replies allowed.