Function which returns an unknown type

99封情书 提交于 2019-12-04 10:53:06

问题


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.


回答1:


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.




回答2:


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.




回答3:


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



回答4:


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



回答5:


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




回答6:


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.




回答7:


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.




回答8:


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



回答9:


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




回答10:


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.




回答11:


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.




回答12:


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.




回答13:


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


来源:https://stackoverflow.com/questions/1358427/function-which-returns-an-unknown-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!