tags:

views:

146

answers:

3
+3  Q: 

C++ "Choice" Union

Not sure if there is a term for this, "choice" seems to work. I'm working in C++, and I have a bunch of unions I need to create where the union represents a choice of one of the members of the union. The current "choice" is tracked and is always available. I am currently coding these "unions" manually, but I'm wondering whether there is any neat trick for doing this sort of thing (semi-)automatically.

I ran into the union limitation of not having assignment operator overloads or non-trival constructors or copy constructors on my first bout of trying to implement this, but realized that because I'm actually tracking the current "choice", there is very defined behavior to take under almost every situation.

Here is what I'm doing right now, (for only two choices, could be up to 10 or 15) and it's quite a significant amount of code nearly all of which is just boilerplate. Also, if anyone has any comments on whether or not what I have below is even valid that would be awesome, still picking up some of the craziness of C++...

struct MyChoice
{
    struct Choice1
    {
        int a;
        char* b;
    };

    struct Choice2
    {
        bool c;
        double d;
    };

    enum Choice
    {
        Choice_Choice1,
        Choice_Choice2
    } choice;

    char _value[max(sizeof(Choice1),sizeof(Choice2))]; // could be private
    Choice1& choice1()
    {
        if(choice == Choice_Choice2)
        {
            (*(Choice2*)_value)->~Choice2();
            (*(Choice1*)_value) = Choice1();
            choice = Choice_Choice1;
        }
        return *(Choice1*)_value;
    }
    Choice2& choice2()
    {
        if(choice == Choice_Choice1)
        {
             (*(Choice1*)_value)->~Choice1();
             (*(Choice2*)_value) = Choice2();
             choice = Choice_Choice2; 
        }
        return *(Choice2*)_value;
    }
    MyChoice()
    {
       _choice = Choice_Choice1;
       (*(Choice1)_value) = Choice1();
    }
    MyChoice(const MyChoice& other)
    {
       this->_choice = other.choice;
       if(this->_choice == Choice_Choice1)
          (*(Choice1*)_value) = other.choice1();
       else
          (*(Choice2*)_value) = other.choice2();
    }
    ~MyChoice()
    {
        if(_choice == Choice_Choice1)
            (*(Choice1)_value)->~Choice1();
        else
            (*(Choice2)_value)->~Choice2();
    }
};

Thanks for your help SO

+13  A: 

Try looking at boost::any and boost::variant. The first one enables you to insert any type in a boost::any variable, tracking its type. It's more a "check-at-runtime" type. The second one force you to define all the types to be inserted (ie boost::variant < Choice1, Choice2, ... > ), but enforce more type-checking at compile time.

Both are used to store object of different types, for example to have heterogeneous containes (std::vector can handle std::string or int for example).

Scharron
boost:variant seems close, would there be any way for me to distinguish between two distinct choices that have the same type? Suppose choice2 and choice3 are both integers, but represent different things, any way to handle this with variant?
LorenVS
You can define your own universal template type wrapper for this, e.g.: `template<int Discriminant, typename Value> struct distinct_type { Value value; }`. Then you can create distinct wrappers as needed: `distinct_type<0, int>`, `distinct_type<1, int>` etc. Better yet, define an `enum` and use its constants for `Discriminant`.
Pavel Minaev
+5  A: 

More generally this is a 'discriminated union' or tagged union. As mentioned boost::variant or boost::any are both implementations of this strategy.

Ron Warholic
thanks for the proper name... wasn't quite sure what to call it
LorenVS
+2  A: 

Even if you are like me and generally prefer variants to inheritance (I'm an ML kind of guy), inheritance is the C++ way to do it.

Instead of using an object of boost::variant<Apple, Pear, Banana>, use a smart pointer to an object of Fruit. Inheritance has the advantage of being open -- you can always add more types of Fruit. Virtual methods are usually much cleaner than switches or if statements. Give inheritance a chance; you'll learn to like it.