Вопрос

In a project I am working in, I need to be able to access the full class name of each and every instance in the program. My approach was declaring a base class from which all the classes would derive and which would have a method that would return the name of the class of the given instance properly demangled. That would look like as follows :

class Base { 
  public: 
  std::string *getClassName() { 
    char *str = (char*) malloc(1024);
    size_t size = 1024;
    int status;
    char *res = abi::__cxa_demangle( typeid(*this).name(), str, &size, &status );
    return new std::string(res);
  };

 class A : public Base { /*...*/ };
 class B : public A { /*...*/ };
 // ... and so on

The problem is that when I access getClassName from, say, an instance of class B, it returns not Base::A::B but just Base, which is the class that is visible from the place where getClassName is declared.

The only solution I found is to make getClassName virtual and force every class to implement it. But that means rewriting always the same code, which I do not want. Do you know any elegant solution to this?

Это было полезно?

Решение

You can just define virtual destructor in Base class:

#include <iostream>
#include <malloc.h>
#include <cxxabi.h>

using namespace std;

class Base {
public:
    virtual ~Base() {}
    std::string getClassName() {
        char *str = (char*) malloc(1024);
        size_t size = 1024;
        int status;
        char *res = abi::__cxa_demangle( typeid(*this).name(), str, &size, &status );
        return std::string(res);
    }
};

class A : public Base { /*...*/ };
class B : public A { /*...*/ };

int main()
{
    A a;
    B b;


    cout << a.getClassName() << endl;
    cout << b.getClassName() << endl;
}

Output:

./test
A
B

BTW in this case it's better let __cxa_demangle to allocate memory (and don't forget to ::free() it because this example code has memory leak)

Другие советы

Make your Base class a template on the derived class and pass to the constructor the derived class's this pointer. Something like:

template <DerivedT> class Base {
    DerivedT * self;
  public:
    Base(DerivedT * s) : self(s) {}
  // ....
};

Then call typeid on *self rather than *this.

Your derived classes will have to become:

template <DerivedT> class A : public Base<DerivedT> { /*...*/ };
class B : public A<B> { /*...*/ };

And pass this up along the constructor chain.

Have a variable that is protected in Class Base, and set the value of it in every derived class, and in getClassName return the same variable..

class Base{
protected:
std:string* _pstrClassName;
}

class A{
 A():_pstrClassName("A"){

 }
} 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top