Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

select an union member depending on a template parameter

I'm dealing with an union in C++, and I would like have a function template which access to the active union member depending on a template parameter.

Code is something like (doSomething is just an example):

union Union {
  int16_t i16;
  int32_t i32;
};

enum class ActiveMember {
    I16 
  , I32
}

template <ActiveMember M>
void doSomething(Union a, const Union b) {
  selectMemeber(a, M) = selectMember(b, M);
  // this would be exactly (not equivalent) the same
  // that a.X = b.X depending on T.
}

To accomplish this I only found bad hacks like specialization, or a not homogeneous way to access and assign.

I'm missing something, and such things should be do it with other approach?

like image 679
user1476999 Avatar asked Jan 17 '13 09:01

user1476999


2 Answers

Possibility 1

instead of using an enum, you can use simple structs to pick the member:

typedef short int16_t;
typedef long int32_t;

union Union {
    int16_t i16;
    int32_t i32;
};

struct ActiveMemberI16 {};
struct ActiveMemberI32 {};

template <typename M>
void doSomething(Union& a, Union b) {
    selectMember(a, M()) = selectMember(b, M());

    // this would be exactly (not equivalent) the same
    // that a.X = b.X depending on T.
}

int16_t& selectMember(Union& u, ActiveMemberI16)
{
    return u.i16;
}

int32_t& selectMember(Union& u, ActiveMemberI32)
{
    return u.i32;
}

int main(int argc, char* argv[])
{
    Union a,b;
    a.i16 = 0;
    b.i16 = 1;
    doSomething<ActiveMemberI16>(a,b);
    std::cout << a.i16 << std::endl;

    b.i32 = 3;
    doSomething<ActiveMemberI32>(a,b);
    std::cout << a.i32 << std::endl;
    return 0;
}

This requires to define a struct and a selectMember method for every member in the union, but at least you can use selectMember across many other functions.

Note that I turned the arguments to references, you might adjust this if not appropriate.

Possibility 2

By casting the union pointer to the desired type pointer, you can go with a single selectMember function.

typedef short int16_t;
typedef long int32_t;

union Union {
    int16_t i16;
    int32_t i32;
};
template <typename T>
T& selectMember(Union& u)
{
    return *((T*)&u);
}

template <typename M>
void doSomething(Union& a, Union b) {
    selectMember<M>(a) = selectMember<M>(b);

    // this would be exactly (not equivalent) the same
    // that a.X = b.X depending on T.
}



int _tmain(int argc, _TCHAR* argv[])
{
    Union a,b;
    a.i16 = 0;
    b.i16 = 1;

    doSomething<int16_t>(a,b);
    std::cout << a.i16 << std::endl;

    b.i32 = 100000;
    doSomething<int32_t>(a,b);
    std::cout << a.i32 << std::endl;
    return 0;
}
like image 68
Philipp Avatar answered Nov 11 '22 15:11

Philipp


I'm not sure why you consider template specialization a "bad hack", but there is no such a thing as "static if" in C++, so if you want your compiler to differentiate the code produced based on the result of an expression evaluated at compile-time, you need to define different, specialized versions of the template.

Here is how you would define it:

#include <iostream>

using namespace std;

union Union {
  int16_t int16;
  int32_t int32;
};

enum class ActiveMember {
    INT16
  , INT32
};

// Declare primary template
template <ActiveMember M>
void doSomething(Union a, const Union b);

// First specialization
template <>
void doSomething<ActiveMember::INT16>(Union a, const Union b)
{
    a.int16 = b.int16;

    // Do what you want here...
    cout << "int16" << endl;
}

// Second specialization
template <>
void doSomething<ActiveMember::INT32>(Union a, const Union b)
{
    a.int32 = b.int32;

    // Do what you want here...
    cout << "int32" << endl;
}

And this is how you would use it.

int main()
{
    Union u1, u2;
    u1.int32 = 0;
    u2.int32 = 0;

    doSomething<ActiveMember::INT16>(u1, u2);
    doSomething<ActiveMember::INT32>(u1, u2);

    return 0;
}
like image 38
Andy Prowl Avatar answered Nov 11 '22 14:11

Andy Prowl