views:

1051

answers:

13

In Java, you can have a List of Objects. You can add objects of multiple types, then retrieve them, check their type, and perform the appropriate action for that type.
For example: (apologies if the code isn't exactly correct, I'm going from memory)

List<Object> list = new LinkedList<Object>();

list.add("Hello World!");
list.add(7);
list.add(true);

for (object o : list)
{
    if (o instanceof int)
        ; // Do stuff if it's an int
    else if (o instanceof String)
        ; // Do stuff if it's a string
    else if (o instanceof boolean)
        ; // Do stuff if it's a boolean
}

What's the best way to replicate this behavior in C++?

+13  A: 

C++ does not support heterogenous containers.

If you are not going to use boost the hack is to create a dummy class and have all the different classes derive from this dummy class. Create a container of your choice to hold dummy class objects and you are ready to go.

class Dummy {
   virtual void whoami() = 0;
};

class Lizard : public Dummy {
   virtual void whoami() { std::cout << "I'm a lizard!\n"; }
};


class Transporter : public Dummy {
   virtual void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {
   std::list<Dummy*> hateList;
   hateList.insert(new Transporter());
   hateList.insert(new Lizard());

   std::for_each(hateList.begin(), hateList.end(), 
                 std::mem_fun(&Dummy::whoami));
   // yes, I'm leaking memory, but that's besides the point
}

If you are going to use boost you can try boost::any. Here is an example of using boost::any.

You may find this excellent article by two leading C++ experts of interest.

Now, boost::variant is another thing to look out for as j_random_hacker mentioned. So, here's a comparison to get a fair idea of what to use.

With a boost::variant the code above would look something like this:

class Lizard {
   void whoami() { std::cout << "I'm a lizard!\n"; }
};

class Transporter {
   void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {

   std::vector< boost::variant<Lizard, Transporter> > hateList;

   hateList.push_back(Lizard());
   hateList.push_back(Transporter());

   std::for_each(hateList.begin(), hateList.end(), std::mem_fun(&Dummy::whoami));
}
dirkgently
yeah i'd go with boost::any too. it's the most fitting library for his purpose (if he wants to use *any* type not just a fixed set of them). any is like 100lines of code so he could create it himself pretty easily)
Johannes Schaub - litb
this paper explains it: http://www.two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf :)
Johannes Schaub - litb
Looks a terrific read -- thanks a ton!
dirkgently
@j_random_hacker: it's still there. did I miss a trick or two? ;)
dirkgently
Why do you call the base class a "dummy" class?
anon
@dirkgently: No, I did :) Then I noticed and deleted the comment... :)
j_random_hacker
'return std::cout << "I'm a lizard!\n";' ? oO
Johannes Schaub - litb
Neil, i think this is only a workaround. you can only store types that are derived from "Dummy". and not an int, for example. Seen that way, Dummy is a cute name for it i think :p boost::any on teh other side can store anything you want.
Johannes Schaub - litb
@dirk Given the OP's question, I think "Object" might be a better choice.
anon
@dirk And I don't see why it is a "workaround" - given the OP's requrements, it seems like the obvious thing to do.
anon
@Neil: Dummy is a dummy for the exact reason litb specified. What name do you suggest?
dirkgently
but i think it makes still sense to limit the range of possible types the container stores. then you can use a variant and say variant< bool, int, double, std::string, boost::shared_ptr<ComplexType> > . what do you think about it?
Johannes Schaub - litb
@litb: That would be a design choice?
dirkgently
yes. probably it doesn't fit his current code :/
Johannes Schaub - litb
A: 

Well, you could create a base class and then create classes which inherit from it. Then, store them in a std::vector.

kitchen
That would have to be "store pointers to them"
anon
Good point Neil.
j_random_hacker
+1  A: 

I am a fairly inexperienced, but here's what I'd go with-

  1. Create a base class for all classes you need to manipulate.
  2. Write container class/ reuse container class. (Revised after seeing other answers -My previous point was too cryptic.)
  3. Write similar code.

I am sure a much better solution is possible. I am also sure a better explanation is possible. I've learnt that I have some bad C++ programming habits, so I've tried to convey my idea without getting into code.

I hope this helps.

batbrat
Don't be too hard on yourself! :) The "base classes + virtual functions" approach is generally the best way to do things if you're designing your system from the ground up. +1.
j_random_hacker
+2  A: 

Sadly there is no easy way of doing this in C++. You have to create a base class yourself and derive all other classes from this class. Create a vector of base class pointers and then use dynamic_cast (which comes with its own runtime overhead) to find the actual type.

Naveen
+1 because dynamic_cast<> closely approximates the asker's "instanceof" approach, but really it's better to use a virtual function instead of a bunch of "if (dynamic_cast<X*>(p)) { ... }" statements. That way you can add all needed functionality in 1 place when adding a new type.
j_random_hacker
On 2nd thought, please change "vector of this base class" to "vector of pointers to base class". Using a vector<Base> as you suggest will just lead to object slicing. (Thanks to Neil Butterworth for pointing this out on a different answer.)
j_random_hacker
+18  A: 

boost::variant is similar to dirkgently's suggestion of boost::any, but supports the Visitor pattern, meaning it's easier to add type-specific code later. Also, it allocates values on the stack rather than using dynamic allocation, leading to slightly more efficient code.

EDIT: As litb points out in the comments, using variant instead of any means you can only hold values from one of a prespecified list of types. This is often a strength, though it might be a weakness in the asker's case.

Here is an example (not using the Visitor pattern though):

#include <vector>
#include <string>
#include <boost/variant.hpp>

using namespace std;
using namespace boost;

...

vector<variant<int, string, bool> > v;

for (int i = 0; i < v.size(); ++i) {
    if (int* pi = get<int>(v[i])) {
        // Do stuff with *pi
    } else if (string* si = get<string>(v[i])) {
        // Do stuff with *si
    } else if (bool* bi = get<bool>(v[i])) {
        // Do stuff with *bi
    }
}

(And yes, you should technically use vector<T>::size_type instead of int for i's type, and you should technically use vector<T>::iterator instead anyway, but I'm trying to keep it simple.)

j_random_hacker
+1: boost::variant looks like a perfect fit for the OP's example. For extra points: it would be nice to show how this example would be written with boost::variant.
Éric Malenfant
+1. You beat me to it :(
dirkgently
Nah! we ought to use `auto`s :D
dirkgently
downside of variant is that it breaks if you try to put a type which is not in the variants union. i think one should mention it in the comments, so i do :) so it is not a perfect fit to the question (which wants to put arbitrary objects) but only for some set of types. but IMHO this is still fine.
Johannes Schaub - litb
@litb: Good point, I'll mention it in the main post too.
j_random_hacker
@dirk: Yes, autos will solve a lot of problems, I can't wait for C++0x! (Or as litb calls it, C++1x.)
j_random_hacker
@j_random_hacker: I call it C++0x where x is a hexadecimal digit ;)
dirkgently
Thanks for the variant suggestion -- I'm not familiar with what the boost libraries have to offer, but after seeing this I think I should spend some time browsing. Picked the visitor variant of this as the answer, though this is still very suitable.
Whatsit
@Whatsit: Yes, using visitors is definitely the best and most maintainable way to work with variants, it's the right choice.
j_random_hacker
+1  A: 

The short answer is... you can't.

The long answer is... you'd have to define your own new heirarchy of objects that all inherit from a base object. In Java all objects ultimately descend from "Object", which is what allows you to do this.

Bryan
Actually you can, sort of. Please see dirkgently's and my answers.
j_random_hacker
Though the approach discussed here *is* used in places. See http://root.cern.ch/ for instance.
dmckee
Ah yes... I should have known Boost would do this. I like David Thornley's answer best though. If you're doing this in C++ code, it basically means you're doing something wrong to begin with.
Bryan
@Bryan: ... or, that the guy who wrote the app you're maintaining and left 3 years ago did something wrong to begin with. That's one reason why hacks like any/variant are sometimes very useful. But certainly, if you're designing a system from scratch, use base classes + virtual functions if you can.
j_random_hacker
+13  A: 

How often is that sort of thing actually useful? I've been programming in C++ for quite a few years, on different projects, and have never actually wanted a heterogenous container. It may be common in Java for some reason (I have much less Java experience), but for any given use of it in a Java project there might be a way to do something different that will work better in C++.

C++ has a heavier emphasis on type safety than Java, and this is very type-unsafe.

That said, if the objects have nothing in common, why are you storing them together?

If they do have things in common, you can make a class for them to inherit from; alternately, use boost::any. If they inherit, have virtual functions to call, or use dynamic_cast<> if you really have to.

David Thornley
+1 Virtual functions were introduced exactly to avoid this kind of code - basically switch on a type flag and do something different in every case branch.
Nemanja Trifunovic
I've worked in a system where the original architect did this (base object class that everything inherits from). It didn't help, and frankly it often got in the way. We've spent years removing it.
Michael Kohne
+1 Maybe I'm a dullard, but I can't imagine why you'd want to store non-related types in the same container. It seems like a bridge you build when you realize you've built your road down the wrong side of a chasm.
Rob K
@Rob K: But sometimes that road on the wrong side of the chasm already exists (e.g. when trying to integrate pre-existing codebases/libraries written by others). RTTI was added to C++ for the same reason (everyone knows it's better to use virtual functions when designing from scratch).
j_random_hacker
@JR: True. In that case you wouldn't want to do it, but you might have to.
Rob K
Yup, as j_random_hacker points out, someone else built this road, I'm just the bridge builder.
Whatsit
Sometimes you can't change the existing code (adding a class that both classes will inherit from) like you described.However, I agree the best solution is creating the inheritance.
Edison Gustavo Muenz
A: 

RTTI (Run time type info) in C++ has always been tough, especially cross-compiler.

You're best option is to use STL and define an interface in order to determine the object type:

public class IThing
{
   virtual bool isA(const char* typeName);
}

void myFunc()
{
   std::vector<IThing> things;

   // ...

   things.add(new FrogThing());
   things.add(new LizardThing());

   // ...

   for (int i = 0; i < things.length(); i++)
   {
       IThing* pThing = things[i];

       if (pThing->isA("lizard"))
       {
         // do this
       }
       // etc
   }
}

Mike

mjmarsh
Hmm. Your interface is fine but it's basically a reimplementation of RTTI, and as I see it, if you want cross-compiler interaction then using this code (as opposed to RTTI) won't help much as each compiler mangles names differently anyway. Can you suggest a case where it would make a difference?
j_random_hacker
I wasn't suggesting the need to call code across multiple compilers. I've just said I've found that different compilers do RTTI differently and since I wasn't sure which one he was using I picked a compiler-agnostic approach.
mjmarsh
Any compiler claiming standard conformance must support the basics such as == and != comparisons between objects of type type_info (the type returned by typeid()). I'm only familiar with g++ and MSVC++, which do this fine, but I'm interested to hear about other compilers that fail in this respect.
j_random_hacker
There was a time when RTTI was "brand new" and lots of implementations didn't support it, but I think (hope?) that things have now changed. Maybe you're referring to embedded environments? (Sometimes these don't even support exceptions!)
j_random_hacker
Yes, I did run into these problems a while back, so maybe conformance on mainstream compilers is now in line with the standards. This would have been about 7 years ago when I was having issues with VC++ vs. g++ implementations of RTTI.
mjmarsh
Ah, that might explain it. MSVC++ came a *long* way in terms of standards conformance in going from version 6 to version 7.1 (and since).
j_random_hacker
+1  A: 

Beside the fact, as most have pointed out, you can't do that, or more importantly, more than likely, you really don't want to.

Let's dismiss your example, and consider something closer to a real-life example. Specifically, some code I saw in a real open-source project. It attempted to emulate a cpu in a character array. Hence it would put into the array a one byte "op code", followed by 0, 1 or 2 bytes which could be a character, an integer, or a pointer to a string, based on the op code. To handle that, it involved a lot of bit-fiddling.

My simple solution: 4 separate stacks<>s: One for the "opcode" enum and one each for chars, ints and string. Take the next off the opcode stack, and the would take you which of the other three to get the operand.

There's a very good chance your actual problem can be handled in a similar way.

James Curran
I had considered this approach, but it strikes me as quite convoluted and hard to maintain without knowing beforehand the approach used.
Whatsit
Also, the ONLY thing that decides the next stack to use would be the type, so I'd need an extra enum to represent that, which again would need to be modified for new types.Fortunately, there are some more elegant solutions presented here.
Whatsit
+7  A: 

Your example using Boost.Variant and a visitor:

#include <string>
#include <list>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost;

typedef variant<string, int, bool> object;

struct vis : public static_visitor<>
{
    void operator() (string s) const { /* do string stuff */ }
    void operator() (int i) const { /* do int stuff */ }
    void operator() (bool b) const { /* do bool stuff */ }  
};

int main() 
{
    list<object> List;

    List.push_back("Hello World!");
    List.push_back(7);
    List.push_back(true);

    BOOST_FOREACH (object& o, List) {
     apply_visitor(vis(), o);
    }

    return 0;
}

One good thing about using this technique is that if, later on, you add another type to the variant and you forget to modify a visitor to include that type, it will not compile. You have to support every possible case. Whereas, if you use a switch or cascading if statements, it's easy to forget to make the change everywhere and introduce a bug.

Ferruccio
+1. variant's visitor facility is *very* nice -- you can even define a single template operator() for unrelated types that support a common interface. (E.g. a templated operator(T t) that returns "t + t" would work on a variant<int, string>.) Nonconforming types can be handled by specialisations.
j_random_hacker
now that this answer appears at the very top, can you please fix it so you define the vis outside main? You cannot define the type locally (local classes cannot be used as template arguments. but yours is (as argument to apply_visitor). otherwise fine i think so +1
Johannes Schaub - litb
@litb - Really? I always define it locally to keep it near the call to apply_visitor().
Ferruccio
@litb - I think you're right. VC++ let me get away with that, g++ is not so forgiving.
Ferruccio
@Ferruccio, oops, sorry. didn't notice you asking me back then :) Yeah in C++03 this isn't possible to pass local classes as type arguments to templates :( Next C++ version will make that possible :)
Johannes Schaub - litb
+2  A: 

Just for completeness of this topic I want to mention that you can actually do this with pure C by using void* and then casting it into whatever it has to be (ok, my example isn't pure C since it uses vectors but that saves me some code). This will work if you know what type your objects are, or if you store a field somewhere which remembers that. You most certainly DON'T want to do this but here is an example to show that it's possible:

#include <iostream>
#include <vector>

using namespace std;

int main() {

  int a = 4;
  string str = "hello";

  vector<void*> list;
  list.push_back( (void*) &a );
  list.push_back( (void*) &str );

  cout <<  * (int*) list[0] << "\t" << * (string*) list[1] << endl;

  return 0;
}
but you can't query the type id which is required by the question. boost::any is the way to go.
obecalp
j_random_hacker
+2  A: 

While you cannot store primitive types in containers, you can create primitive type wrapper classes which will be similar to Java's autoboxed primitive types (in your example the primitive typed literals are actually being autoboxed); instances of which appear in C++ code (and can (almost) be used) just like primitive variables/data members.

See Object Wrappers for the Built-In Types from Data Structures and Algorithms with Object-Oriented Design Patterns in C++.

With the wrapped object you can use the c++ typeid() operator to compare the type. I am pretty sure the following comparison will work: if (typeid(o) == typeid(Int)) [where Int would be the wrapped class for the int primitive type, etc...] (otherwise simply add a function to your primitive wrappers that returns a typeid and thus: if (o.get_typeid() == typeid(Int)) ...

That being said, with respect to your example, this has code smell to me. Unless this is the only place where you are checking the type of the object, I would be inclined to use polymorphism (especially if you have other methods/functions specific with respect to type). In this case I would use the primitive wrappers adding an interfaced class declaring the deferred method (for doing 'do stuff') that would be implemented by each of your wrapped primitive classes. With this you would be able to use your container iterator and eliminate your if statement (again, if you only have this one comparison of type, setting up the deferred method using polymorphism just for this would be overkill).

Roger Nelson
In the real case, there is only one place the type is being checked, but thanks for the suggestions and references.
Whatsit
+2  A: 

I'd just like to point out that using dynamic type casting in order to branch based on type often hints at flaws in the architecture. Most times you can achieve the same effect using virtual functions:

class MyData
{
public:
  // base classes of polymorphic types should have a virtual destructor
  virtual ~MyData() {} 

  // hand off to protected implementation in derived classes
  void DoSomething() { this->OnDoSomething(); } 

protected:
  // abstract, force implementation in derived classes
  virtual void OnDoSomething() = 0;
};

class MyIntData : public MyData
{
protected:
  // do something to int data
  virtual void OnDoSomething() { ... } 
private:
  int data;
};

class MyComplexData : public MyData
{
protected:
  // do something to Complex data
  virtual void OnDoSomething() { ... }
private:
  Complex data;
};

void main()
{
  // alloc data objects
  MyData* myData[ 2 ] =
  {
    new MyIntData()
  , new MyComplexData()
  };

  // process data objects
  for ( int i = 0; i < 2; ++i ) // for each data object
  {
     myData[ i ]->DoSomething(); // no type cast needed
  }

  // delete data objects
  delete myData[0];
  delete myData[1];
};