0

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".

MayNotBe
  • 2,110
  • 3
  • 32
  • 47

3 Answers3

2

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.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • 2
    Good point, but union Bar still remains a *private* type of Foo. – Fox Oct 02 '14 at 14:36
  • 1
    Ignoring the attempt to externally use a private member type of a class, upvote for actually answering the question. – Serge Oct 02 '14 at 14:47
  • @Fox Which in this case [is fine,](http://coliru.stacked-crooked.com/a/f2cc1a5f16e453a8) because it only gets accessed from another `Foo` member. Probably not what OP wants, yeah. – jrok Oct 02 '14 at 14:51
  • @Serge Externaly, where? :) See the link in my previos comment. – jrok Oct 02 '14 at 14:52
  • @jrok Well, what I mean was (and I can't really back this claim up) that the access modifier for getBar at least implies it could be accessed from some other class. But, seriously, that was just a footnote relative to the syntax issues that were corrected. Really, all I mean is, it looks good =) – Serge Oct 02 '14 at 15:11
  • This got to the root of my question. Thank you. – MayNotBe Oct 02 '14 at 15:33
0

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++.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • Could you please explaing why @jrok? :) – gsamaras Oct 02 '14 at 14:25
  • 1
    Tuple is a data type holding multiple values. A union can hold one value of one type at a time. – jrok Oct 02 '14 at 14:27
  • Correct @jrok. However, the OP didn't know about tuple and I think that he switch to that for easiness. If not, I have stated in my answer what the problem is with his code. However, I will update my answer, with your information. Thanks! – gsamaras Oct 02 '14 at 14:31
  • Your example using my code doesn't account for passing a specific Foo and returning it's Bar. Also, I just made the union public and it's still not recognizing the type 'Bar' – MayNotBe Oct 02 '14 at 14:45
  • Yes @MayNotBe, because you can't get the private member of `Foo`, without a public getter. The problem is that are now writing the public member, so that's not possible. So you don't want to use a tuple? – gsamaras Oct 02 '14 at 14:50
  • This is completely wrong. A tuple and a union share pretty much nothing. Also `Bar Foo::getBar() ` still wouldn't compile because it's missing `Foo::` in front of `Bar`, being `Bar` an "inner class" of `Foo`. – Shoe Oct 02 '14 at 14:53
  • Well I didn't want just to write an answer that states the problem and actually doesn't help much, that's why I proposed this workaround. You are right @Sofffia, I am updating. I think the OP tries to solve something and he doesn't quite succeeds in it, that's why I proposed tuple. – gsamaras Oct 02 '14 at 14:56
  • @G.Samaras You proposed a workaround based on what, exactly? OP didn't specify the actual problem at hand. Recommending the use of a `std::deque` seems just as useful. – Shoe Oct 02 '14 at 14:58
  • @Sofffia on the logic that (s)he wants something that can hold different data types. The OP can treat the tuple as a union in his code. I didn't think of the `std::deque`. – gsamaras Oct 02 '14 at 15:00
  • @G.Samaras What. `std::deque` is just the first thing that came to my mind. It was just an example to show you how random you suggestion is. "on the logic that (s)he wants something that can hold different data types" -- This is so generic that you could recommend anything, including a `struct`. – Shoe Oct 02 '14 at 15:03
0

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.

Fox
  • 2,078
  • 17
  • 18
  • It would be nice if you provided an example which uses your class. :) – gsamaras Oct 02 '14 at 14:26
  • really? downvoting because of that?? – Fox Oct 02 '14 at 14:35
  • I downvoted because this is an extension to an answer [which doesn't seem an answer to me](http://stackoverflow.com/questions/26162556/getter-for-private-union-c#comment41017416_26162623). Also to extend an answer you should post a comment, not another answer. – Shoe Oct 02 '14 at 14:59
  • @Sofffia, thanks for clarifying your downvote. I posted it as an answer given the volume of code I (thought I) had to write. Is there a community convention when responding to answers with a voluminous response? – Fox Oct 02 '14 at 15:01
  • @Fox I don't remember. I'm pretty sure there was a clarification on that regard on Meta. The point is that I don't consider valid the answer you are trying to extend. – Shoe Oct 02 '14 at 15:05
  • I dont dispute that. Thanks again. – Fox Oct 02 '14 at 15:06