tags:

views:

88

answers:

5

Hi, I need to find the type of object pointed by pointer. Code is as below.

//pWindow is pointer to either base Window object or derived Window objects like //Window_Derived.
const char* windowName = typeid(*pWindow).name(); 
if(strcmp(windowName, typeid(Window).name()) == 0)
{
  // ...
}
else if(strcmp(windowName, typeid(Window_Derived).name()) == 0)
{
  // ...     
}

As i can't use switch statement for comparing string, i am forced to use if else chain. But as the number of window types i have is high, this if else chain is becoming too lengthy. Can we check the window type using switch or an easier method ?

EDIT: Am working in a logger module. I thought, logger should not call derived class virtual function for logging purpose. It should do on its own. So i dropped virtual function approach.

A: 

You might try putting all your typeid(...).name() values in a map, then doing a find() in the map. You could map to an int that can be used in a switch statement, or to a function pointer. Better yet, you might look again at getting a virtual function inside each of the types that does what you need.

Tony
Am working in a logger module. I thought, logger should not call derived class virtual function for logging purpose. It should do on its own. So i dropped virtual function approach.
bjskishore123
Ummm... why would you think that? I can't see any reason to avoid it, and as you can see it's causing hassles....
Tony
+6  A: 

First of all use a higher level construct for strings like std::string.
Second, if you need to check the type of the window your design is wrong.
Use the Liskov substitution principle to design correctly.
It basically means that any of the derived Window objects can be replaced with it's super class.
This can only happen if both share the same interface and the derived classes don't violate the contract provided by the base class.
If you need some mechanism to apply behavior dynamically use the Visitor Pattern

the_drow
Actually, using a `std::string` here isn't important, after all there is no ownership issue.
Matthieu M.
@Matthieu M.: True, but that was a general tip from an experienced C++ developer to a new one. Using std::string is important, it simplifies your handling of strings a lot.
the_drow
+1, the Visitor pattern is the right way to go here.
Blindy
@Blindy - Visitor may be the right way to go. It's either visitor, or adding a virtual method to the base class.
Omnifarious
+1. Can't vouch for the visitor pattern (hate patterns in general), but the overall spirit that the design is wrong stands. In general in C++, polymorphism is the official mechanism to avoid the switch/case on types. If one tries to code switch/case, then something is missing in the inheritance tree.
Dummy00001
+1  A: 

Create a dictionary (set/hashmap) with the strings as keys and the behaviour as value.

Using behaviour as values can be done in two ways:

  1. Encapsulate each behaviour in it's own class that inherit from an interface with"DoAction" method that execute the behavior
  2. Use function pointers

Update: I found this article that might be what you're looking for: http://www.dreamincode.net/forums/topic/38412-the-command-pattern-c/

Dror Helper
A: 

What you ask for is possible, it's also unlikely to be a good solution to your problem.

Effectively the if/else if/else chain is ugly, the first solution that comes to mind will therefore to use a construct that will lift this, an associative container comes to mind and the default one is obviously std::unordered_map.

Thinking on the type of this container, you will realize that you need to use the typename as the key and associate it to a functor object...

However there are much more elegant constructs for this. The first of all will be of course the use of a virtual method.

class Base
{
public:
  void execute() const { this->executeImpl(); }
private:
  virtual void executeImpl() const { /* default impl */ }
};

class Derived: public Base
{
  virtual void executeImpl() const { /* another impl */ }
};

It's the OO way of dealing with this type of requirement.

Finally, if you find yourself willing to add many different operations on your hierarchy, I will suggest the use of a well-known design pattern: Visitor. There is a variation called Acyclic Visitor which helps dealing with dependencies.

Matthieu M.
I do not believe that `::std::unordered_map` is part of the standard. Maybe you mean `::std::tr1::unordered_map`?
Omnifarious
@Omnifarious: it depends on which standard, it's part of `C++0x` final draft. Effectively you can always have, depending on your STL, `std::hash_map`, `std::unordered_map`, `std::tr1::unordered_map` or `boost::unordered_map` (if you don't have anything in your STL). I just answer with C++0x in mind these days, unless a specific version of a compiler is specified.
Matthieu M.
+1  A: 

Here are the things to do in order of preference:

  1. Add a new virtual method to the base class and simply call it. Then put a virtual method of the same name in each derived class that implements the corresponding else if clause inside it. This is the preferred option as your current strategy is a widely recognized symptom of poor design, and this is the suggested remedy.
  2. Use a ::std::map< ::std::string, void (*)(Window *pWindow)>. This will allow you to look up the function to call in a map, which is much faster and easier to add to. This will also require you to split each else if clause into its own function.
  3. Use a ::std::map< ::std::string, int>. This will let you look up an integer for the corresponding string and then you can switch on the integer.

There are other refactoring strategies to use that more closely resemble option 1 here. For example,if you can't add a method to the Window class, you can create an interface class that has the needed method. Then you can make a function that uses dynamic_cast to figure out if the object implements the interface class and call the method in that case, and then handle the few remaining cases with your else if construct.

Omnifarious