views:

103

answers:

3

Hi all! I would like to ask you for an advice about function template. I have a function that adds some data into buffer. But I need also to add an information about data type into the buffer. The type of data is a following enum:

enum ParameterType
{
   UINT,
   FLOAT,
   DOUBLE
};

And I need to create a function template from function like this:

void SomeBuffer::append( double par )
{
    appendType( DOUBLE );
    memcpy( pStr + _length, &par, sizeof( double ) );
    _length += sizeof( double );
    appendType( DOUBLE );
}

Could you please advice me how to pass a value from ParameterType for appendType() depending on type of parameter.

template<class T>
void SomeBuffer::append( T par )
{    
    appendType( ??? );
    memcpy( pStr + _length, &par, sizeof( T ) );
    _length += sizeof( T );
    appendType( ??? );
}

I tried to do it by some macros but without success. Thank you very much for any advice.

+4  A: 
template <typename T> struct identity { };

inline void appendType_(identity<double>  ) { appendType(DOUBLE);  }
inline void appendType_(identity<unsigned>) { appendType(UINT);    }
inline void appendType_(identity<MyType>  ) { appendType(MY_TYPE); }

Then use it like so:

template<class T>
void SomeBuffer::append( T par )
{    
    appendType_( identity<T>() );
    memcpy( pStr + _length, &par, sizeof( T ) );
    _length += sizeof( T );
    appendType_( identity<T>() );
}

You could also combine this with @vitaut's idea of getting the type code separately, and passing it to appendType.

inline ParameterType typeCode(identity<double>  ) { return DOUBLE;  }
inline ParameterType typeCode(identity<unsigned>) { return UINT;    }
inline ParameterType typeCode(identity<MyType>  ) { return MY_TYPE; }
...
appendType(typeCode(identity<T>()));

EDIT: Thanks to @Johannes for the identity<T> suggestion.

Marcelo Cantos
in this case plain overloads might make more sense than complete specializations. See http://www.gotw.ca/publications/mill17.htm
awoodland
@awoodland: Thanks for the tip. I had just gotten to that realisation when your comment popped up.
Marcelo Cantos
This solution has the problem that, due to promotions when calling `appendType_` or `typeCode`, an incorrect type code gets inserted, for example when calling `append` for some class type that has an implicit conversion to `unsigned int`.
Bart van Ingen Schenau
You can fix @Bart's point by using `identity<T>` like this: `inline ParameterType typeCode(identity<double>) { return DOUBLE; } ...` then you call it with `appendType(typeCode(identity<T>()));`.
Johannes Schaub - litb
+7  A: 

You can do what you want by introducing an additional class template that will map the type to the enumeration constant that you need as in the following example (without FLOAT for brevity):

enum ParameterType
{
   UINT,
   DOUBLE
};

template <typename T>
struct GetTypeCode;

template <>
struct GetTypeCode<double>
{
    static const ParameterType Value = DOUBLE;
};

template <>
struct GetTypeCode<unsigned>
{
    static const ParameterType Value = UINT;
};

template <typename T>
void SomeBuffer::append(T par)
{
    appendType(GetTypeCode<T>::Value);
    memcpy(pStr + _length, &par, sizeof(T));
    _length += sizeof(T);
    appendType(GetTypeCode<T>::Value);
}

Since specializations of GetTypeCode will be almost identical you may introduce a macro for defining them, e.g.

#define MAP_TYPE_CODE(Type, ID) \
template <> \
struct GetTypeCode<Type> \
{ \
    static const ParameterType Value = ID; \
};

MAP_TYPE_CODE(double, DOUBLE)
MAP_TYPE_CODE(unsigned, UINT)
vitaut
+2  A: 

A different approach to that given by Marcelo Cantos would be creating a metafunction:

template <typename T>
struct my_type_id;    // undefined as to trigger compiler error for unknown types
template <>
struct my_type_id<double> {
   static const ParameterType value = DOUBLE;
};
template <>
struct my_type_id<float> {
   static const ParameterType value = float;
};

And then using that to resolve the enumerated value:

template<class T>
void SomeBuffer::append( T par )
{    
    appendType( my_type_id<T>::value );
    memcpy( pStr + _length, &par, sizeof( T ) );
    _length += sizeof( T );
    appendType( my_type_id<T>::value );
}

The actual definition of the traits can be defined in a macro:

#define TYPE_ID_MAP( type, val ) \
   template <> struct my_type_id<type> { \
      const static ParameterType value = val;\
   }
template <typename T>
struct my_type_id;    // undefined as to trigger compiler error for unknown types
TYPE_ID_MAP( double, DOUBLE );
TYPE_ID_MAP( float, FLOAT );
TYPE_ID_MAP( unsigned int, UINT );
David Rodríguez - dribeas