user3279954
user3279954

Reputation: 586

C++: template function to retrieve value from an union

I am writing a template to get value from a collection that can hold different data types. for ex a variant structure. But i get a warning when the compiler generates code for assignments with other types. for ex

struct Variant
{
    enum Type
    {
        _bool,
        _int
    };
    Type type;

    union VAL
    {
        bool bVal;
        int nVal;
    }val;
};

template <typename ValType>
void GetValue(Variant v, ValType val)
{
    if(v.type == Variant::_bool)
    {
        val = v.val.bVal;
    }
    else if(v.type == Variant::_int)
    {
        val = v.val.nVal; // C4800
    }
}

Variant v;
v.type = Variant::_bool;
v.val.bVal = true;

bool bVal(false);
GetValue(v, bVal);

Warning:

warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)

Can someone suggest on how can the template be re written to get value the right way ?

Upvotes: 1

Views: 1125

Answers (2)

Barry
Barry

Reputation: 302767

First, as dyp already suggested in the comments, I would recommend looking at Boost.Variant.

That said, the issue you're running into is that regardless of what type you're storing internally, every branch always has to be compiled the way you wrote it - and some of those branches might not make any sense (what if you added an array or some other POD type?) The way Boost solves this issue is by requiring the user to pass in a functor, and you call that functor with the right type:

template <typename F>
void visit(Variant v, F visitor)
{
    if(v.type == Variant::_bool) {
        visitor(v.val.bVal);
    }
    else if(v.type == Variant::_int) {
        visitor(v.val.nVal);
    }
    // etc.
}

So then, you just write a visitor:

struct get_bool {
    void operator()(bool b) {
        bVal = b;
    }

    template <typename OTHER>
    void operator()(OTHER ) { }

    bool& bVal;
};

And use it:

bool bVal(false);
visit(v, get_bool{bVal});

Upvotes: 1

Pixelchemist
Pixelchemist

Reputation: 24936

In your template for GetValue for ValType === bool, val is of type bool. Thus the line val = v.val.nVal; results in the warning.

In order to provide special code for special types, there is template specialization.

You can specialize the function template like

template<class T>
void GetValue(Variant v, T & val);

template<>
void GetValue<int>(Variant v, int & val)
{
  if (v.type == Variant::_int)
    val = v.val.nVal;
}
template <>
void GetValue<bool>(Variant v, bool & val)
{
  if (v.type == Variant::_bool)
    val = v.val.bVal;
}

Note: v.type does not determine which one will be called, the argument does.

Upvotes: 3

Related Questions