1

I have a class AnimalInterface in Animal namespace:

namespace Animal
{
    class AnimalInterface
    {
    public:
        AnimalInterface() = default;
        virtual ~AnimalInterface() = default;

        virtual std::string makeSound() = 0;
    };
}

Suppose I have 2 classes implementing from this interface

class Dog : public AnimalInterface
{
public:
    Dog() = default;
    ~Dog() = default;

    std::string makeSound()
    {
        return "Bark";
    }
};

class Cat : public AnimalInterface
{
public:
    Cat() = default;
    ~Cat() = default;

    std::string makeSound()
    {
        return "Meow";
    }
};

Now I have a class from where I call these functions. I would like to be able to get the class names of the animals from the objects using run time type information.

void AnimalSounds
{
    list<unique_ptr<AnimalInterface>> animals;
    animals.push_back(make_unique<Dog>());
    animals.push_back(make_unique<Cat>());

    for (const auto& animal : animals)
    {

        cout << typeid(animal).name() << " makes sound: " << animal->makeSound() << endl;
    }
}

I expect the output to be

Dog makes sound: Bark
Cat makes sound: Meow

Instead, I get the following

class std::unique_ptr<class Animal::AnimalInterface,struct std::default_delete<class Animal::AnimalInterface> > makes sound: Bark
class std::unique_ptr<class Animal::AnimalInterface,struct std::default_delete<class Animal::AnimalInterface> > makes sound: Meow

How do I fix this? Thanks!

Fatih BAKIR
  • 4,569
  • 1
  • 21
  • 27
  • *I would like to be able to get the class names of the animals from the objects using reflection.* -- There is no reflection in C++ at this time. This looks like an [XY Problem](http://xyproblem.info/) – PaulMcKenzie Oct 14 '20 at 05:47
  • The string returned by `typeid` is implementation defined. https://stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c – Retired Ninja Oct 14 '20 at 05:47
  • 1
    Think about what type `animal` is. Is it really `Dog` or `Cat`? You can't rely on `typeid(...).name()` anyway, it's output is neither garantueed to be the class name, nor to be consistend (not even between executions of one and the same program). – Lukas-T Oct 14 '20 at 05:50
  • How about -- `std::unordered_map>` or if animals can have duplicate names `std::unordered_multimap>`? – PaulMcKenzie Oct 14 '20 at 05:52

3 Answers3

4

You need to dereference the pointer. That way you get the RTTI information from the object that it points to, not the pointer itself:

const auto& animal_ref = *animal;
cout << typeid(animal_ref).name() << " makes sound: " << animal->makeSound() << endl;
3Dog makes sound: Bark
3Cat makes sound: Meow

Note that you will still have to deal with implementation-defined names as described in the comments. You can use your library's demangle function for that:

int status;
const auto& animal_ref = *animal;
const auto name = abi::__cxa_demangle(typeid(animal_ref).name(), 0, 0, &status);
cout << name << " makes sound: " << animal->makeSound() << endl;
Dog makes sound: Bark
Cat makes sound: Meow

Unless this question was asked for a better understanding of RTTI or for debugging purposes, I would still strongly advise you to reconsider your approach. This is nothing that your code should rely on.

(And yes, technically even demangling might not be enough, and the name might change between invocations. In the implementations that I have worked with, this never happened. At the point where we are talking about cats making meow, this is more a technicality than an actual issue.)

mrks
  • 8,033
  • 1
  • 33
  • 62
1

I think the unexpected problem you have is that your animals list contains unique_ptr to Animals. Therefore, when you iterate over that list, each animal in the loop is a unique_ptr<Animal>, which is a template with default arguments. To at least fix that problem, dereference the pointer before using typeid:

for (const auto& animal : animals)
{
  cout << typeid(*animal).name() << " makes sound: " << animal->makeSound() << endl;
}

However, what you get here is still implementation defined. Read up on RTTI for more information.

Fatih BAKIR
  • 4,569
  • 1
  • 21
  • 27
0

1.) animal is a reference to some element from animals. What does animals contain? It contains unique_ptr<AnimalInterface>, so the compiler prints exactly the right thing, just a bit verbose.

2.) typeif(...).name() is not really refelction. In particular it doesn't give you any garantues about the result whatsoever. See also the reference:

No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.

3.) I can only think of one robust and standard compliant way to achieve what you want to: another virtual function that returns the animal kind. Or some compiler specific extension, but standard C++ doesn't offer any real reflection at the moment.

Lukas-T
  • 11,133
  • 3
  • 20
  • 30