views:

589

answers:

13
class Test
{
public:

 SOMETHING DoIt(int a)
 {
  float FLOAT = 1.2;
  int INT = 2;
  char CHAR = 'a';

  switch(a)
  {
  case 1: return INT;
  case 2: return FLOAT;
  case 3: return CHAR;
  }
 }
};


int main(int argc, char* argv[])
{  
 Test obj;
 cout<<obj.DoIt(1);    
    return 0;
}

Now, using the knowledge that a = 1 implies that I need to return an integer, etc., is there anyway Doit() can return a variable of variable data type?

Essentially, with what do I replace SOMETHING ?

PS: I'm trying to find a an alternative to returning a structure/union containing these data types.

A: 

If the user knows what is put in, you could use a template to fix this. If not, I can't think of any solution.

martiert
Could you be more specific? The only variable here is the function return type.
Jacob
You can't use templates because the return type depends on run-time condition.
AraK
@AraK: Only if the `int` isn't known at compile-time. If it is, you can. See http://stackoverflow.com/questions/1358427/function-which-returns-an-unknown-type/1358966#1358966
sbi
+21  A: 

You can use boost::any or boost::variant to do what you want. I recommend boost::variant because you know the collection of types you want to return.


This is a very simple example, though you can do much more with variant. Check the reference for more examples :)

#include "boost/variant.hpp"
#include <iostream>

typedef boost::variant<char, int, double> myvariant;

myvariant fun(int value)
{
 if(value == 0)
 {
  return 1001;
 }
 else if(value  == 1)
 {
  return 3.2;
 }
  return 'V';
}

int main()
{
 myvariant v = fun(0);
 std::cout << v << std::endl;

 v = fun(1);
 std::cout << v << std::endl;

 v = fun(54151);
 std::cout << v << std::endl;
}

The output:

1001
3.2
V

I would use boost::variant instead of a union because you can't use non-POD types inside union. Also, boost::any is great if you don't know the type you are dealing with. Otherwise, I would use boost::variant because it is much more efficient and safer.


Answering the edited question: If you don't want to ship Boost with your code, take a look at bcp. The description of bcp from the same link:

The bcp utility is a tool for extracting subsets of Boost, it's useful for Boost authors who want to distribute their library separately from Boost, and for Boost users who want to distribute a subset of Boost with their application.

bcp can also report on which parts of Boost your code is dependent on, and what licences are used by those dependencies.

AraK
Beat me to it.
Jonas
To make this a better answer for didactic purposes, could you include links to the reference pages for boost::any and boost::variant? Thanks!
A. Levy
Arak, could you add the bcp comment to your answer?
Jacob
@Jacob I haven't really used **bcp** before because Boost is always my in my toolbox. I ll add a link to it though :)
AraK
+7  A: 

C++ is a strongly-typed language, and has no concept of an unknown type. You could try using boost::any, which can (sort of) specify any type. I would question the design of your function, however.

anon
+1 the need for this kind of behavior can likely be replaced with better design.
luke
Probably, being the keyword.
Jacob
Jacob- if you describe what you're actually trying to accomplish, we may be able to suggest a better approach.
luke
That would be too complicated. But the sample code I've posted is quite indicative of what I'd like to accomplish: I need to return some data using a key (i.e. the int a) and the returned data can have a data type from a known finite set.
Jacob
@Jacob The quality of the answers you get here is directly proportionate to the detail of the question.
anon
Humor us and at least say what the set is for. :)
GMan
@Neil While I understand there is a code smell here. I can't understand how would you question the design without knowing the design in the first place. I used boost::variant when building a lexer for one of my college classes, and it was the right tool in my opinion. Even if C++ is a strongly typed language that doesn't mean in my opinion it is wrong all the time to use variant or any from boost.
AraK
@Arak I said I would question his design - he doesn't seem to want to answer, however. Question means exactly that - "Please explain why you think this is a good solution?" All designs should be questioned, all the time.
anon
@Neil: There's nothing more to elaborate on. And the quality of an answer certainly depends on the detail of the question, but I think it might have something to do with the person answering the question as well :)
Jacob
@Neil Pardon my poor English ;)
AraK
@Jacob Or asking it, presumably?
anon
Oh, most definitely. But looking at **most** of the innovative answers posted in this thread, I don't think that's the problem here.
Jacob
All these answer's could be stuck in a local minimum, where the real answers lies elsewhere. While `boost::any` or `boost::variant` may be the best way to return varying types, returning varying types might not be the best way to solve your specific problem.
GMan
As I've repeated before, and now for the last time: this is the specific problem. The rest of the project has **does not matter**.
Jacob
So your entire project consists of calling one function, getting a variant return type and stopping? What part of "this might not be the best way of solving the big picture" don't you get?
GMan
I will not be pursuing this line of discussion any further since it has degenerated to poop-flinging and seems to have reached a point where a stubborn refusal to see past one's point of view has rendered any future discourse pointless.
Jacob
According to your definitions. I don't see any "poop-flinging", just simple statements. How can you learn anything if you think you know everything?
GMan
@GMan: He never said he knows everything. He only said that the design of his problem, the reasons for which he wouldn't reveal, leads to this question. And while I agree with Neil that the design is questionable (in the sense of "we should ask questions about it"), since there is a `boost::any` and a `boost::variant`, (AraK and) I might not be alone in seeing valid designs that come to this problem. And, no, I don't agree that "of course, you are a design genius before whom we should all bow" is a simple statement. It's offensive.
sbi
@Neil: Yes, he really _might_ be in love with his design too much to have it dissected here. OTOH he might not have any influence over it or/and he might not be allowed to reveal it here. If he says he won't reveal the reasons, who are we to question this? He asked a question, some of us answered that it probably shouldn't be necessary to ask such questions. If he still believes asking was necessary, then that's all there is. This site _is_ for asking questions and deciding for an answer, after all. FWIW, I consider your last comment as quite offensive and I have marked it as such.
sbi
+3  A: 

Use boost::any:

boost::any DoIt(int a)
{
    float FLOAT = 1.2;
    int INT = 2;
    char CHAR = 'a';

    switch(a)
    {
    case 1: return boost::any(INT);
    case 2: return boost::any( FLOAT);
    case 3: return boost::any( CHAR);
    }
}
Cătălin Pitiș
A: 

You could use a union:

typedef union {
  int i;
  float f;
  char c;
} retType;

retType DoIt(int a){
  retType ret;

  float FLOAT = 1.2;
  int INT = 2;
  char CHAR = 'a';

  switch(a)
  {
    case 1: ret.i = INT; break;
    case 2: ret.f = FLOAT; break;
    case 3: ret.c = CHAR; break;
  }
  return ret;
}
Marc
He wants an alternative to unions. See the P.S. at the end of the question.
A. Levy
+1  A: 

You could use a struct containing a void* pointing to the value you want returned along with a size_t that indicates the size of the object being returned. Something like this:

struct Something {
    void *value;
    size_t size;
};

Remember that the void* should point to a value residing on the heap (i.e. dynamically allocated using new or malloc) and the caller should take care of freeing the allocated object.

Having said that, I think it's a bad idea overall.

Edit: You may also want to consider including a flag indicating what was returned in the above structure so that the caller can make sense of it, unless the caller knows what type to expect.

Vulcan Eager
A: 

SOMETHING = void*

You have to cast the returned value, so you have to know what is returned.

void* DoIt(int a)
    {
        float FLOAT = 1.2;
        int INT = 2;
        char CHAR = 'a';

        switch(a)
        {
        case 1: return &INT;
        case 2: return &FLOAT;
        case 3: return &CHAR;
        }
    }
Jay
Just remember that the variables FLOAT, INT and CHAR will go out of scope once the function returns. I am not sure what the consequences are.
Vulcan Eager
-1 for returning addresses of local values. In addition a void* is rather useless if you don't know what type it actually refers to.
UncleBens
@Agnel: The consequences are severe.
sbi
The consequences are undefined, so nothing to severe.
GMan
Good catch uncle ben.
Jay
@GMan: I consider Nasal Demons as severe. `:)`
sbi
+1  A: 

The usual way to achieve something like this is C, which doesn't always work in C++, is with a union and a type field:

enum SomeType { INT, FLOAT, CHAR };
struct Something
{
    SomeType type;
    union
    {
        int i;
        float f;
        char c;
    };
};

Something DoIt(int a)
{
    Something s;
    switch (a)
    {
      case 1:
        s.type = INT;
        s.i = 2;
        break;
      case 2:
        s.type = FLOAT;
        s.f = 1.2;
        break;
      case 3:
        s.type = CHAR;
        s.c = 'a';
        break;
      default:
        // ???
    }
    return s;
}

This doesn't work in C++ when one of the possible value types is a class with a non-trivial constructor, because it wouldn't always be clear which constructor should be called. Boost.Variant uses a more complex version of this approach to provide this kind of construct for any value types in C++.

Nathan Kitchen
Alternative to a union/struct
Jacob
A: 

The Adobe Source Libraries also has adobe::any_regular_t, which allows you to store any type as long as it models the Regular concept. You would wrap your return value much the same way you would with boost::any. (There is also documentation on the linked page as to how adobe::any_regular_t differs from boost::any -- of course the type you pick should depend on the requirements of your code.)

fbrereto
A: 

You could pass by reference instead and be typesave and check if it worked at the same time, would not involve any additional library either (your kind of ansi C++ solution):

bool DoIt (int i, int & r1)
{
  if (i==1) {r1 = 5; return true}
  return false;
}

bool DoIt (int i, double & r2)
{
  if (i==2) {r2 = 1.2; return true}
  return false;
}

...

I find this solution often more clean in terms of design. It's unfortunate that funciton signatures don't allow multiple types as return types, but this way you can pass anything.

count0
This is part of an application where it is essential to use it directly without creating a new variable (to which we'd pass the reference).
Jacob
A: 

I think the problem is about this function design. Have you tried overloading?

class Test
{

public:

int DoIt(int a) {

  int INT = 2;
   return INT;

} 

float DoIt(float a) {

float FLOAT = 1.2; 
return FLOAT;

} 

char DoIt(char a) {

char CHAR = 'a'; 
return CHAR;

} 

};


int main(int argc, char* argv[])
{       
    Test obj;

//....

switch(a)
case 1: 
    cout<< obj.DoIt(1);    
break;

case 2:
cout<< obj.DoIt(1.01);   
break;

case 3:
cout<< obj.DoIt("1");   
break;

    return 0;
}

Inside DoIt functions you can place more code and make them call other functions for not repeating code.

yelinna
The varying element here is the function return type and not the arguments.
Jacob
+4  A: 

EDIT: boost::any using bcp (thanks AraK) seems to be the best solution to date but is it possible to prove (to some extent) that there exists no ANSI C++ solution to this problem?

You seem a bit confused about the terminology here.

First, let's call it ISO C++, shall we? It was standardized by ISO in 1998, and since then, that is what people have referred to when talking about "standard C++". Now, what do you mean by an "ANSI C++ solution"?

  • A solution that compiles cleanly using only ANSI (or ISO) C++? If so, Boost is the ANSI C++ solution
  • A solution already implemented in the ANSI C++ standard library? If so then no, no such solution exists (and there is no "proof", other than "go read through the language standard and see if you can find such a class. If you can't, it isn't there".
  • A solution you could implement yourself using only ANSI C++. Then the answer is "yes, you could go copy the source code from Boost".

I can't imagine what kind of "proof" you'd be looking for. C++ is a document in prose form. It is not a mathematical equation. It can not be "proven", except by saying "go read the standard". Proving that something is defined in the language or in the standard library is easy -- simply point out where in the standard it is described. But proving that something isn't there is basically impossible -- except by enumerating every single sentence of the standard, and document that none of them describe what you're looking for. And I doubt you'll find anyone willing to do that for you.

Anyway, the correct standard C++ solution is to use Boost. It is not a heavy-weight solution. Boost is pretty lightweight in that you can include exactly the bits you need, with no dependencies on the rest of the library collection.

From what you've described (a light application for a broad user base), there is zero reason not to use Boost. It can simplify your code and reduce the number of bugs caused by attempting to reinvent the wheel. When distributing the compiled executable, it has zero cost. The Boost.Any library is, like much of Boost, header-only, and is simply compiled into your executable. No separate libraries have to be distributed.

There is nothing to be gained by trying to reinvent the wheel. Your executable will be no smaller or more efficient, but it will be more buggy.

And I'm willing to bet that your home-brewed solution will not be ANSI C++. It will rely on some form of undefined behavior. If you want an ANSI-C++ solution, your best bet is Boost.

jalf
I suspect that they meant without relying on boxing the returned value, given their PS.
Boojum
perhaps, but if so, the answer is wonderfully simple "nope, doesn't exist". Although the request for "proof" still puzzles me.
jalf
[This][1] is an example of a proof showing that this person's answer will not work. IMO, a proof is just a collection of *proven* statements, used to prove the question at hand. The proof I was asking for could've been the part of the documentation you keep talking about which explicitly does not allow this in any form (which I think is hard to find). [1]: http://stackoverflow.com/questions/1358427/function-which-returns-an-unknown-type/1358495#1358495
Jacob
And I was looking for a simple answer (like most of the alternatives posted here) which I could've missed. If that's impossible, and boost::any is the best alternative, then that's fine by me, I'll use bcp and add it (like I've stated in my post).
Jacob
Why would the language specification describe what is *not* defined? It tells you what you *can* do in the language. If something is not mentioned in the standard, then it is not part of the language.In general, the only way to show that something can *not* be done in the language is by failing to find it in the specification. The standard does not say "no variant type exists", it simply describes the types that *do* exist. You sound like you want a mathematical proof, which isn't really possible or meaningful in this context.
jalf
The link you posted doesn't "prove" anything. It shows a possible solution, and a comment mentions that it is not legal. That's not proof of anything. The commenter *could* be wrong (he isn't, but he *could* be, and so it doesn't prove anything).
jalf
Yeah, nobody said that proofs have to be correct. But that doesn't make it any less of a proof. I was just curious if there were any one-shot statements which would show that this isn't possible. E.g. It is better to compare floating point number with epsilon rather than directly comparing them. Why? Due to inherent limitation of how floating point numbers are represented, etc. A proof doesn't always have to contain an equation, you know ( http://www.math.csusb.edu/notes/proofs/pfnot/node4.html )
Jacob
Jacob, I suggest you take a logic class, or something. Your reasoning is terrible. Nobody had to say proofs are correct? An argument could be valid and unsound, if that's what you mean, but by definition an unsound argument has no value. Likewise, an invalid argument has no value. A proof is a collection premises, along with logical connection, that prove a conclusion *to be true*. This is like saying, "2 + 2 = 5 isn't correct, but it's still a valid math equation!"
GMan
And if you want proof, just make a dichotomy. Either it's in the standard, or it's not. Something is valid ISO/ANSI C++ *if and only if* it is in the standard. Therefore, if it is not in the standard, it is not valid ISO/ANSI C++.
GMan
And thirdly, the link you stated has nothing to do with your proposition. A proposition that is true remains true no matter how it is communicated, as long as the communication method is sufficient. So, it might not be written in mathematical notation, but anything you say can be transposed into mathematical notation. Your link simply describes different methods of proof, not a proof that proofs need not contain mathematical equations.
GMan
I hate stating the obvious, but I guess it has to be done now. Everybody writes a proof thinking that it's correct, but it is quite possible that someone will come along and disprove it, hence my statement about proofs not being correct. Human error is quite common, you know ..
Jacob
But it is no longer called a proof once it's shown incorrect. Your statement was "Yeah, nobody said that proofs have to be correct. But that doesn't make it any less of a proof. ", which is very wrong. It **does** make it less of a proof. In fact, it makes it not a proof at all.
GMan
Once it's shown to be incorrect, it's not a proof. But before that happens, isn't it still a so-called proof? And if people aren't allowed to post such proofs due to reasons not stated here, then how can other people disprove them?
Jacob
In any case, I have my answer as jalf stated in his original post. The edit in the original question is now a moot point since the boost implementation exists (proof by example).
Jacob
I have no idea what you just said. I never said people can't post proofs. If a proof is incorrect, it can't be regarded as a proof any longer, just an invalid statement.
GMan
+3  A: 

If you know type at compile time you could use templates. If type depends on run-time, then using templates is not an option.

class Test
{
  template<int> struct Int2Type {};
  template<>    struct Int2Type<1> { typedef int value_type; };
  template<>    struct Int2Type<2> { typedef float value_type; };
  template<>    struct Int2Type<3> { typedef char value_type; };

public:
  template<int x> typename Int2Type<x>::value_type DoIt() {}; // error if unknown type used
  template<> typename Int2Type<1>::value_type DoIt<1>() { return 2; };
  template<> typename Int2Type<2>::value_type DoIt<2>() { return 1.2f; };
  template<> typename Int2Type<3>::value_type DoIt<3>() { return 'a'; };
};

int main()
{
  Test obj;
  cout << obj.DoIt<2>(); 
  return 0;
}
Kirill V. Lyadvinsky
This is a beautifully simple solution if the numbers are known at compile-time. +1
sbi