Search code examples
c++templatesmemberunions

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?


Solution

  • 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;
    }