MayNotBe
MayNotBe

Reputation: 2140

Getter for private union - c++

I have a class Foo with a private union:

class Foo
{
  public:
    static Foo foo(int type);
    static Foo foo(double type);
    static Foo foo(bool type);
    static Bar getBar(Foo foo);
  private:
    union Bar
       {
          int iBar;
          double rBar;
          bool bBar;
        } bar;
};

Can I write a generic getter that returns the appropriate Bar?

I've tried:

Bar Foo::getBar(Foo foo) 
  {
    return foo.bar;
  }

And other variations but the compiler doesn't recognize a type named "Bar" in "Foo".

Upvotes: 0

Views: 593

Answers (3)

gsamaras
gsamaras

Reputation: 73424

Use a tuple.

Example:

// tuple's get
#include <iostream>
#include <tuple>

int main ()
{
  std::tuple<int,char> mytuple (10,'a');

  std::get<0>(mytuple) = 20;

  std::cout << "mytuple contains: ";
  std::cout << std::get<0>(mytuple) << " and " << std::get<1>(mytuple);
  std::cout << std::endl;

  return 0;
}

Notice that if you had two elements, you could use a std::pair.

In the updated question, you need to forward declare the union Bar, like this:

class Foo {
  union Bar;
 public:
  static Foo foo(int type);
  static Foo foo(double type);
  static Foo foo(bool type);
  static Bar getBar(Foo foo);
 private:
  union Bar {
    int iBar;
    double rBar;
    bool bBar;
  } bar;
};

Moreover, what you do is not acceptable, because bar is a private member of Foo.


Note that a tuple and a union are not exactly the same things. "Tuple is a data type holding multiple values. A union can hold one value of one type at a time.", as the comment suggests. That means that tuple's data can co-exist, while union's data can not.


So, in case you don't want to use a tuple, but stick to a union, you need to change your class and make the union visible to the rest of the world. Now that it is private, even when you get it, with the getter function, the world outside your class can't see the union, thus can't use it. But then, the thing gets a bit complicated, so I don't see why not to use a tuple.


A good example for unions can be found here. It's in C, as expected, since Unions are more frequent in C, than in C++.

Upvotes: 0

Fox
Fox

Reputation: 2138

To expand on G.Samaras's answer, you are allowed to do this:

typedef std::tuple<type0, type1, type2, type3, type4> MyTuple;
MyTuple myTuple;
type0 a = std::get<0>(myTuple);
type1 b = std::get<1>(myTuple); //...etc.

(edit: stupid me... as it turns out, you are also allowed to do this:

type0 a = std::get<type0>(myTuple);
type1 a = std::get<type1>(myTuple);

... leaving the rest of the answer in place as an example of how NOT to assume things)

So how do you associate 0 with type0 and so on? You do this (untested, but should work):

class MyTupleWrapper
{
  private:
     template <typename T> class TypeOffset {};
     template <> class TypeOffset<type0> { enum { value = 0; } };
     template <> class TypeOffset<type1> { enum { value = 1; } };
     template <> class TypeOffset<type2> { enum { value = 2; } };
     template <> class TypeOffset<type3> { enum { value = 3; } };
     template <> class TypeOffset<type4> { enum { value = 4; } };
     // ...etc
  public:
     typedef std::tuple<type0, type1, type2, type3, type4> MyTupleType;
     explicit MyTupleWrapper(const MyTupleType& type) : _type(type) {}
     template <typename T>
     const T& Get() { return std::get< TypeOffset<typename T>::value >(_type); }
  private:
     MyTupleType _type;
}

To break this construct down without going too much into implementation, it is this:

a. You have two tools - the std::tuple<Type1, Type2, ...> specialized type and the std::get<integer>(tupleObject); to get specific types out of it. The integer param depends on the initial way you defined the tuple... so if your number is 3, the return value is the third type in your list of types inside that tuple (in our case, type3)

b. The tuple itself supports normal assignment... so MyTupleType t; t = type1(); is allowed. But you can't invoke type1 a = t; - it needs to be type1 a = std::get<1>(t); which is stupid, because you can have many tuple types and you shouldn't need to remember which position you defined type1 in each tuple type.

c. What this wrapper does (intends to do?) is the ability to say type1 a = t.Get<type1>(); using template overloading to convert each type at compile time into its offset.

Upvotes: 0

jrok
jrok

Reputation: 55425

Two things - unlike data and funcion members, member types need to be declared before they are used, and, you need to fully qualify the nested type in out-of-class definitions:

class Foo {
    // here, compiler doesn't yet know what Bar is
    union Bar {
        int iBar;
        double rBar;
        bool bBar;
    } bar; // now it does
public:
    static Bar getBar(Foo); // so use it
};

   Foo::Bar Foo::getBar(Foo foo) { return foo.bar; }
// ^^^^^

You'll probably also want to make Bar public if it's to be of any practical use. Hope that helps.

Upvotes: 2

Related Questions