tags:

views:

468

answers:

13

Hi

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.

+12  A: 

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;


}
anthares
how about the three classes are totally unrelated?
skydoor
Note that in C++ you will need to return a *pointer* to the base type.
anon
@skydoor That would be bad design, to say the least.
anon
Without pointers, your return values are subject to *object slicing*.
Georg Fritzsche
If they are totally unrelated you have written an extremely strange function. So strange that many of your SO peers would consider your design to be at fault, or even, to be blunt, just plain wrong.
High Performance Mark
I was right @Neil Butterworth thinks you have designed your function badly, or worse.
High Performance Mark
Maybe, maybe not. I don't think the OP should *always* use inheritance in this case. Always and Never rules make me nervous.
Kristo
This is one of those things... as Jon Skeet said in one post, you can do that if you are really willing to defend it in a peer review and come up with really good reasons why the different classes must be unrelated.
David Rodríguez - dribeas
As @gf said, your objects will get sliced, which is bad. If you go the common base class approach, you'll have to return pointers or references.
jalf
As I said, this is not a code, ready for use. It depends on what languages you write it, it will need some tuning.
anthares
Voted down, because you forgot the pointers (shared_ptr, preferably)
e.tadeu
@e.tadeu I said this is not a C++ code...
anthares
@High-Performance Mark: you were right because Neil Butterworth agrees with you? @Neil Butterworth: is `boost::variant<boost::recursive_wrapper<branch>, leaf> node;` where leaf and branch have no common virtual base really so bad? IOW, is runtime-polymorphism-based programming the only answer to trees?
just somebody
+2  A: 

In order to be able to do anything useful with the result, you have to return an object which has a common baseclass. In your case you might want to let A, B, and C inherit from a common "intersection-class"; a class which is common for all objects which represents some form of intersection. Your function f would then return an object of this type.

JesperE
+2  A: 

The classes you want to return should have a common parent class or interface.
If those classes do not have anything in common, that, I suppose, is untrue, you can return object.
This feature is also known as polymorphism.

Li0liQ
+1  A: 

You can't. You can only return a base pointer to different derived classes. If this is absolutely, 100% needed, you can use exceptions as a ugly hack, but that's obviously not recommended at all.

gparent
Never say can't. You can do it in various ways, though I hadn't considered exceptions.
anon
Better solutions have been expressed by other people in this thread than using exceptions, either way :)
gparent
+1  A: 

In c++ base class pointer can point to derived class object. We can make use of this fact to code a function that meets your requirements:

class shape{};

class circle: public shape
{};

class square: public shape
{};

shape* function(int i){ // function returning a base class pointer.

    switch(i) {

        case 1: return new circle(); 

        case 2: return new square();

    }
}
codaddict
+25  A: 

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)

Manuel
Interesting approach, and better than `struct R { enum type {t1,t2,t3}; union { A a; B b; C c; } value; }`...
xtofl
+1. The visitor struct has the added benefit of failing to compile if you don't handle one of the cases.
Kristo
@xtofl, having checked the sources, I wouldn't say "better". `boost::variant` follows the same idea, but just adds syntactic sugar.
Pavel Shved
@Pavel: Nah, there's a much bigger difference than syntactic sugar. `boost::variant` actually works in the general case.. The simple union approach only works with types that can be stored in unions (meaning they must not have nontrivial constructors or destructors, for example)
jalf
`boost::variant` is an ugly monster... heavyweight, with lots of template black magic underlying it. For him, a simple shared_ptr to a base class would suffice.
e.tadeu
@e.tadeu - I've updated my answer in response to your criticism. Hope that makes your reconsider your stance towards Boost.Variant.
Manuel
A: 

In languages that reflection, it is easier to achieve. In cpp, if you have a standard set of classes to be returned (pointers), create an enumeration and return the enum value. Using this value you can infer the class type. This is a generic way in case there is no common parent class

questzen
+1  A: 

Even if you could return three different types of objects from the function, what would you do with the result? You need to do something like:

XXX ret_val = getIntersection();

If getIntersection returned three different types of objects, XXX would have to change based on what getIntersection was going to return. Clearly this is quite impossible.

To deal with this, you can define one type that defines enough to cover all the possibilities:

class Intersection { 
    enum { empty, point, circle, sphere};
    point3D location;
    size_t radius;
};

Now getIntersection() can return an Intersection that defines what kind of intersection you have (and BTW, you need to consider the fourth possibility: with two spheres of the same radius and same center point, the intersection will be a sphere) and the size and location of that intersection.

Jerry Coffin
+2  A: 

There is one other option available. You can return a union of pointers to objects along with a tag that tells the caller which member of the union is valid. Something like:

struct result {
    enum discriminant { A_member, B_member, C_member, Undefined } tag;
    union result_data {
        A *a_object;
        B *b_object;
        C *c_object;
    } data;
    result(): tag(Undefined) {}
    explicit result(A *obj): tag(A_member) { data.a_object = obj; }
    explicit result(B *obj): tag(B_member) { data.b_object = obj; }
    explicit result(C *obj): tag(C_member) { data.c_object = obj; }
 };

I would probably use Boost.variant as suggested by Manuel if you have the option.

D.Shawley
+5  A: 

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();
  }
}
Alexander Poluektov
I'd rather like to see that `print_any` function.
Novelocrat
Code is updated.
Alexander Poluektov
Rather lenghty, and certainly less self-documenting that a `variant` since anything can be returned... also, there's the performance overhead to consider, `any` requires runtime check of the type using RTTI while `variant` stores the type in a data member.
Matthieu M.
@Matthieu: certainly which one to use depends on use case ;)
Alexander Poluektov
Yes, Boost.Variant cannot be used if you don't know the types in advance :)
Matthieu M.
A: 

The limitation is based on the declared return type of your method. Your code states:

f() {
    in case one: return A;
    in case two: return B;
    in case three: return C;
}

When in reality the compiler requires something like this:

FooType f() {
    in case one: return A;
    in case two: return B;
    in case three: return C;
}

It must be possible to convert the A, B, and C to a FooType, typically through simple inheritance, though I won't get into the differences between subclasses vs subtyping.

There are approaches that can get around this. You could create a class or struct (C++) which has fields for each different type of possible return and use some flag field to indicate which field is the actual returned value.

class ReturnHolder {
    public int fieldFlag;
    public TypeA A;
    public TypeB B; 
    public TypeC C;
}

The enum example in another answer is more of the same. The reason why that is a hack is that the code that handles the return from this method will have to have lots of code to handle each of the different possibilites, like so

main(){

FooType *x = new FooType();
ReturnHolder ret = x.f();

switch (ret.fieldFlag)
    case: 1
        //read ret.A

    case: 2
        //read ret.B

    case: 3
        //read ret.C

}

And that's without even going into trying to do it with Exceptions which introduce even bigger problems. Maybe I'll add that in later as an edit.

Kelly French
+1  A: 

And by the way, as you said that question "is not limited to C++":

1) dynamic languages, of course, make it piece of cake:

# python
def func(i):
  if i == 0:
    return 0
  elif i == 1:
    return "zero"
  else
    return ()

2) some functional languages (Haskell, OCaml, Scala, F#) provide nice built-in variants that are called Algebraic Data Types (article has good samples).

Alexander Poluektov
A: 

You really shouldn't want to be doing that, and should really come up with a better design instead of forcing a square peg in a round hole. And with most languages you can't do it at all, by design. You will never really know what you are working with, and neither will the compiler ahead of time, ensuring extra bugs and weird behavior and incomprehensibility.

Charles Eli Cheese