[ACCEPTED]-Function which returns an unknown type-variant

Accepted answer
Score: 29

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


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

#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 15 you can't use non-POD types inside union. Also, boost::any is 14 great if you don't know the type you are 13 dealing with. Otherwise, I would use boost::variant because 12 it is much more efficient and safer.


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

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

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

Score: 8

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

Score: 6

If you know type at compile time you could 2 use templates. If type depends on run-time, then 1 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;
}
Score: 3

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

0

Score: 3

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

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

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

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

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

Score: 3

The usual way to achieve something like 9 this is C, which doesn't always work in 8 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 7 doesn't work in C++ when one of the possible 6 value types is a class with a non-trivial 5 constructor, because it wouldn't always 4 be clear which constructor should be called. Boost.Variant uses 3 a more complex version of this approach 2 to provide this kind of construct for any 1 value types in C++.

Score: 3

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

You seem a bit confused about the 41 terminology here.

First, let's call it ISO 40 C++, shall we? It was standardized by ISO 39 in 1998, and since then, that is what people 38 have referred to when talking about "standard 37 C++". Now, what do you mean by an "ANSI 36 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 35 of "proof" you'd be looking for. C++ is 34 a document in prose form. It is not a mathematical 33 equation. It can not be "proven", except 32 by saying "go read the standard". Proving 31 that something is defined in the language 30 or in the standard library is easy -- simply 29 point out where in the standard it is described. But 28 proving that something isn't there is basically 27 impossible -- except by enumerating every single 26 sentence of the standard, and document that 25 none of them describe what you're looking 24 for. And I doubt you'll find anyone willing 23 to do that for you.

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

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

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

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

Score: 1

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

0

Score: 0

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

Score: 0

You could pass by reference instead and 8 be typesave and check if it worked at the 7 same time, would not involve any additional 6 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 5 find this solution often more clean in terms 4 of design. It's unfortunate that funciton 3 signatures don't allow multiple types as 2 return types, but this way you can pass 1 anything.

Score: 0

As of C++17 there is std::any and std::variant, which means 3 you do not need third party library for 2 this. From @Arak's answer the code will 1 be modified a bit as below.

#include <variant>
#include <any>
#include <iostream>

typedef std::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;
}

More Related questions