Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return different classes from one function?

Tags:

c++

I have a question, though it is not limited to C++. How to return totally different class from one function?

f() {

in case one: return A;
in case two: return B;
in case three: return C;


}

For example, I have two balls in the space, according to the position and the size, there are three situations for the two balls to intersect with each other, i.e, non-intersection, at point, a and circle. How can I return different class in one function?

Thanks.

like image 277
skydoor Avatar asked Feb 10 '10 15:02

skydoor


People also ask

Can a method have 2 return types?

Your answerNo, you don't have two return types.

Can a Java function return different types?

With generic Java collections, we can return multiple values of a common type. The collections framework has a wide spectrum of classes and interfaces.


3 Answers

If you can afford Boost then this sounds like a perfect application for Boost.Variant.

struct NoIntersection {
    // empty
};
struct Point { 
    // whatever
};
struct Circle { 
    // whatever
};

typedef boost::variant<NoIntersection, Point, Circle> IntersectionResult;

IntersectionResult intersection_test() {

    if(some_condition){ 
        return NoIntersection();
    }
    if(other_condition){ 
        return Point(x, y);
    }
    if(another_condition){ 
        return Circle(c, r);
    }
    throw std::runtime_error("unexpected");
}

You then process your result with a static visitor:

 struct process_result_visitor : public boost::static_visitor<> {
    
     void operator()(NoIntersection) {
        std::cout << "there was no intersection\n";
     }
     void operator()(Point const &pnt) {
        std::cout << "there was a point intersection\n";
     }
     void operator()(Circle const &circle) {
        std::cout << "there was a circle intersection\n";
     }
 };

 IntersectionResult result = intersection_test();
 boost::apply_visitor(process_result_visitor(), result);

EDIT: The visitor class must derive from boost::static_visitor

UPDATE: Prompted by some critical comments I've written a little benchmark program. Four approaches are compared:

  • boost::variant
  • union
  • class hierarchy
  • boost::any

These are the results in my home computer, when I compile in release mode with default optimizations (VC08):

test with boost::variant took 0.011 microseconds

test with union took 0.012 microseconds

test with hierarchy took 0.227 microseconds

test with boost::any took 0.188 microseconds

Using boost::variant is faster than a union and leads (IMO) to the most elegant code. I'd guess that the extremely poor performance of the class hierarchy approach is due to the need to use dynamic memory allocations and dynamic dispatch. boost::any is neither fast nor especially elegant so I wouldn't consider it for this task (it has other applications though)

like image 176
Manuel Avatar answered Dec 05 '22 00:12

Manuel


The classes you want to return should be derived from a common base class. So, you can return the base type. For Example (this is not a code, just marking the pattern, you can use an interface if your language supports this abstraction or abstract class for example. If you use C++ you will have to return a pointer of the common class):

class A : public Common
{
..
}

class B : public Common
{
..
}

class C : public Common
{
..
}

Common f() {

in case one: return A;
in case two: return B;
in case three: return C;


}
like image 31
anthares Avatar answered Dec 04 '22 23:12

anthares


In addition to @Manuel's Boost.Variant suggestion, take a look at Boost.Any: has similar purpose as Boost.Variant but different tradeoffs and functionality.

boost::any is unbounded (can hold any type) while boost::variant is bounded (supported types is encoded in variant type, so it can hold only values of these types).

// from Beyond the C++ Standard Library: An Introduction to Boost 
// By Björn Karlsson 

#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "boost/any.hpp"

class A {
public:
  void some_function() { std::cout << "A::some_function()\n"; }
};

class B {
public:
  void some_function() { std::cout << "B::some_function()\n"; }
};

class C {
public:
  void some_function() { std::cout << "C::some_function()\n"; }
};

int main() {
  std::cout << "Example of using any.\n\n";

  std::vector<boost::any> store_anything;

  store_anything.push_back(A());
  store_anything.push_back(B());
  store_anything.push_back(C());

  // While we're at it, let's add a few other things as well
  store_anything.push_back(std::string("This is fantastic! "));
  store_anything.push_back(3);
  store_anything.push_back(std::make_pair(true, 7.92));

  void print_any(boost::any& a);
  // Defined later; reports on the value in a

  std::for_each(
    store_anything.begin(),
    store_anything.end(),
    print_any);
}

void print_any(boost::any& a) {
  if (A* pA=boost::any_cast<A>(&a)) {
    pA->some_function();
  }
  else if (B* pB=boost::any_cast<B>(&a)) {
    pB->some_function();
  }
  else if (C* pC=boost::any_cast<C>(&a)) {
    pC->some_function();
  }
}
like image 40
Alexander Poluektov Avatar answered Dec 04 '22 23:12

Alexander Poluektov