typeid help

I have a kind of pointless question but I'd still love to know why it does this.

consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>
using namespace std;

class Object
{
};

int main()
{
if(typeid(Object).name() == "class Object")
cout << "They're equal";



};


nothing shows up. but if change:
 
if(typeid(Object).name() == "class Object")


with:

1
2
3
string s = typeid(Object).name();
if(s == "class Object")
cout << "They're equal."


it works! So if typeid is returning a string, why doesn't it work in the first example?


And another question: is typeid good practice to use? For example, for checking weither an object is a "weapon" or "armor" (both derived from a base class Object). or should I look into something else?
Last edited on
> So if typeid is returning a string

typeid(Object).name() returns a const char* (null-terminated character string).

typeid(Object).name() == "class Object" compares two pointers.

Caveat:
Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given, in particular, the returned string can be identical for several types and change between invocations of the same program. http://en.cppreference.com/w/cpp/types/type_info/name
checking weither an object is a "weapon" or "armor" (both derived from a base class Object)
You could always dynamic_cast and see if it returns or not. Though there might be better ways like using a std::map if weapons and armor are in same container or keep the weapons and armor in different containers.
If I understand right, you're saying when I do:
 
string s = typid(Object).name();


it's converting the const char* into a string? Whereas when I do:
if(typeid(Object).name() == "class Object")
I'm trying to compare two different pointers?


@giblit
I'm not sure how to do what i want to do. If I make a base class called Object, and 2 derived classes named Armor and Weapon, and make a vector of Object pointers, the vector would only be able to see what's in the Object (duh). But not every Object will have the same stats.

For example, Object might have a member called "string name", so Weapon and armor will have a member called name (which I want, and which it will be), but Armor doesn't need a member called "damage". And Weapon won't need a member called "defense" needed by Weapon. But the vector of objects won't see either of those two members because the class Object doesn't have either in the first place.

The most obvious way to fix this would make mulitple vectors each for different Object types, but wouldn't that be bad coding? Here's an example about what I'm talking about:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Object
{
protected:
	string name;
	int worth;
	int weight;
}; //Example base class all the derived classes from this class will need every member from Object (string name, int worth, etc

class Weapon : public Object
{
private:
	int damage;
	

}; //Weapon class. has int damage which none other class needs BUT Weapon.

class Armor : public Object
{
private:
	int defense;
}; //again, has a member called defense no other class needs. 


But a vector of Objects won't see any of the members added by Weapon and Armor. but I need to to apply the stats to the player. I hope you can see my delay. :(
Last edited on
> If I understand right, you're saying when I do: string s = typid(Object).name();
> it's converting the const char* into a string?

Yes.


> Whereas when I do: if(typeid(Object).name() == "class Object")
> I'm trying to compare two different pointers?

Yes.



> But not every Object will have the same stats.
> but I need to to apply the stats to the player.

One option is to have a virtual function which returns the attributes as a collection of name-value pairs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <memory>

struct Object
{
    Object( std::string name, int worth, double weight ) : name(name), worth(worth), weight(weight) {}
    virtual ~Object() = default ;

    using attribute_map = std::map< std::string, std::string > ; // name-value pairs

    virtual attribute_map attributes() const
    { return { { "name", name }, { "worth", std::to_string(worth) }, { "weight", std::to_string(weight) } }; }

    std::string name;
    int worth;
    double weight;
}; //Example base class all the derived classes from this class will need 
   // every member from Object (string name, int worth, etc

struct Weapon : public Object
{
    Weapon( std::string name, int worth, double weight, int damage ) : Object(name,worth,weight), damage(damage) {}
    int damage;

    virtual attribute_map attributes() const override
    { 
        auto result =  Object::attributes() ;
        result.emplace( "damage", std::to_string(damage) ) ; 
        return result ;
    }
}; //Weapon class. has int damage which none other class needs BUT Weapon.

struct Armor : public Object
{
    Armor( std::string name, int worth, double weight, short defense ) : Object(name,worth,weight), defense(defense) {}
    short defense;
    
    virtual attribute_map attributes() const override
    { 
        auto result =  Object::attributes() ;
        result.emplace( "defense", std::to_string(defense) ) ; 
        return result ;
    }
}; //again, has a member called defense no other class needs.

int main()
{
    std::vector< std::unique_ptr<Object> > objects ;
    objects.emplace_back( new Object( "one", 2, 3.3 ) ) ;
    objects.emplace_back( new Weapon( "two", 4, 5.5, 6 ) ) ;
    objects.emplace_back( new Armor( "three", 7, 8.8, 9 ) ) ;
    objects.emplace_back( new Weapon( "four", 10, 11.1, 12 ) ) ;
    objects.emplace_back( new Armor( "five", 13, 14.4, 15) ) ;
    
    int total_worth = 0, total_damage = 0, total_defense = 0 ;
    double  total_weight = 0.0 ;
    
    for( const auto& ptr : objects ) 
    {
         auto attrib = ptr->attributes() ;
         
         std::cout << attrib["name"] << ' ' ;
         
         total_worth += std::stoi(attrib["worth"]) ;
         total_weight += std::stod(attrib["weight"]) ;
         
         if( !attrib["damage"].empty() ) total_damage += std::stoi(attrib["damage"]) ;
         if( !attrib["defense"].empty() ) total_defense += std::stoi(attrib["defense"]) ;
    }
    
    std::cout << "\ntotal_worth: " << total_worth << "\ntotal_weight: " << total_weight  
              << "\ntotal_damage: " << total_damage << "\ntotal_defense: "  << total_defense << '\n' ; 
              
    // apply stats to player          
}

http://coliru.stacked-crooked.com/a/65e22020a0d0429d

Other options include:

Use the visitor pattern to recover lost type information
http://sourcemaking.com/design_patterns/visitor

Use a mediator to update the player stats from the object. (the mediator performs a dynamic_cast).
http://sourcemaking.com/design_patterns/mediator
Hmm. Thank you for the well written reply. I don't understand a lot of what you just posted, and I'd hate to use code I don't understand, so maybe I can go another route for right now. Do you think having different vectors of different item types would be a bad thing? There'd only be about 3 - 4 types of them.

Thanks again, you beautiful stud.
Shouldn't be that big of a deal to have a vector for armor, weapons, accessories, ect.. Though if you have too many different types that's when it could get annoying having 1000 different containers in one class.
There is a very good article I found helpful on this site about type erasure which may help you: http://www.cplusplus.com/articles/oz18T05o/
That looks like component based entity system.
> Do you think having different vectors of different item types would be a bad thing?
> There'd only be about 3 - 4 types of them.

No, I don't think that it would necessarily be a bad thing.

The big plus is the extreme simplicity of the design. The drawback would be that adding a new derived class would break existing code.
Topic archived. No new replies allowed.