4

Context:
We're trying to set up a class template, named Operand, which could take several types as its typename T. Those are defined in the following enum:

enum eOperandType {
    INT8
    INT16,
    INT32,
    FLOAT,
    DOUBLE
};

Those correspond to the types defined in <cstdint>, that is, int8_t, int16_t, and so on.

The constructor must be Operand(std::string const & value);.

template<class T>
class Operand : public IOperand
{

public:
    Operand(std::string const & value)
    {
        std::stringstream ss(value);

        ss >> _value;
        //_type = ??? ;
    }

[...]

private:
    Operand(void){}

    eOperandType    _type;
    T               _value;
};

The interface IOperand is nothing important here, just some prototypes for operator overloads.

Question:
What's the best way to set the _type attribute? The easy way would be to just write several if/else if with typeid or something close to that, but I feel like that would be pretty dirty. Furthermore, I just think that using typeid inside of a template just means that you're doing something wrong somewhere... right?

Zulu
  • 8,765
  • 9
  • 49
  • 56
Nathanael C.
  • 73
  • 1
  • 7

3 Answers3

5

Use a helper class to deduce the value of _type.

template <typename T> struct OperandType;

template <> struct OperandType<int8_t>
{
    static const eOperandType t = INT8;
};

template <> struct OperandType<int16_t>
{
    static const eOperandType t = INT16;
};

etc.

and use it as:

Operand(std::string const & value) : type_(OperandType<T>::t)
{
   ...
}

PS

Given that you can deduce the value of type_ any time you need it, does it make sense to store it as a member variable?

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • To answer your PS: the IOperand has getters for those attributes, I just figured I should store them there. For the actual answer, we figured it was something like this, but weren't sure it was a good thing to do. – Nathanael C. May 12 '15 at 21:27
  • 1
    @NathanaelC., it is better to avoid storing redundant data. Storing redundant can be justified only when it is necessary to remove a performance bottleneck. YMMV. – R Sahu May 12 '15 at 21:30
  • Didn't think about that, makes perfect sense :) Thanks! – Nathanael C. May 12 '15 at 21:34
4

You can use template overloads. If you specialize the template parameter for each of the types you can set a specific parameter for you can have a specific value in the specialized template. You can then use that value for the _type attribute. So if you do something like this

template<typename _Ty> struct OperandSelector;

template<> struct OperandSelector<int8_t> {
    static const eOperandType value = INT8;
}

and then create another specialization for each of the values you want to use, in your case INT16, INT32, FLOAT and DOUBLE. To set the value of _type you would then assign to it with the value of OperandSelector<T>::value like this

_type = OperandSelector<T>::value;

this approach would allow the selection to be done at compile time and make selecting the value a simple copy.

phantom
  • 3,292
  • 13
  • 21
0
template<class...>struct types{using type=types;};

template<class E, E...es>
using enums=std::integer_sequence<E,es...>;

template<class types, class T, class enums>
struct pick;

template<class T0, class...Ts, class T, class E, E e0, E...es>
struct pick< types<T0, Ts...>, T, enums<E,e0,es...>>:
  pick< types<Ts...>, T, enums<E,es...>>
{};

template<class T0, class...Ts, class E, E e0, E...es>
struct pick< types<T0, Ts...>, T0, enums<E,e0,es...>>:
  std::integral_constant<E,e0>
{};

then

using c_types = types<int8_t, int16_t, int32_t, float, double>;
using e_types = enums<eOperandType, INT8, INT16, INT32, FLOAT, DOUBLE>;

and in the class itself:

static const eOperandType _type = pick<c_types, T, e_types>::value;

this won't work with many 100s of types.

Here I made it static const, because it cannot vary, so why let it?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524