Click here to Skip to main content
15,882,017 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
What I want:

I want that all of my child classes are able to return their name as string (using a common function GetName()).
The GetName function has to be accessible through the base class pointer, returning the name of the specific child class.


My idea:

1. I use an abstract baseclass with a pure virtual function GetName() to force all child classes to implement this function.

class Base
{
	const char* GetName() const =0;	
}


2. To prevent to actually having to implement this function for all child classes I use the curiously recurring template pattern to "inherit" the implementation of the GetName function for each child class:

template<typename SpecificClass>
class ClassNameInfo
{
	const char* GetName() const
	{
		return typeid(SpecificClass).name();
	}
}


3. The child classes inherit from Base (to be accessible through a common base class pointer) and from ClassNameInfo to "inherit" an implementation of GetName() for this specific class:

class ChildA : public Base, public ClassNameInfo<ChildA>
{
//...
}

class ChildB : public Base, public ClassNameInfo<ChildB>
{
//...
}


Problem:

Inheriting an implementation for a pure virtual function does not seem to work. Any ideas?
Posted
Updated 20-Jun-10 1:28am
v3

Just put a public method in the base class that returns the name of the object represented by the this pointer. It doesn't even have to be pure virtual.
 
Share this answer
 
Comments
Aescleal 20-Jun-10 13:23pm    
At least one function in the base class has to be virtual though.
Typeinfo is bound statically when a class doesn't have any virtual functions - the compiler works out which typeinfo to use at compile time. If you have one virtual function then the type_info is looked up at runtime from the v_table. If you define base the way you do in the third answer or CPalini did in the second you'll not end up with the correct type_info for a class. Bung on one virtual function and all will be well.

To illustrate this:

#include <typeinfo>
#include <string>

template<typename T>
std::string type_name_string( const T *p )
{
    return typeid( *p ).name();
}

class A
{
};

class B: public A
{
};

int main()
try
{
    B b;
    std::cout << type_name_string( static_cast<const A *>( &b ) ) << std::endl;
}
catch( std::exception &e )
{
    std::cout << "Something went wrong: " << e.what() << std::endl;
}
catch( ... )
{
    std::cout << "Something went wrong, no idea what!" << std::endl;
}


On VC++ 2010 this always prints "class A."

Now add a virtual function to A...

#include <typeinfo>
#include <string>

template<typename T>
std::string type_name_string( const T *p )
{
    return typeid( *p ).name();
}

class A
{
    public:
        virtual ~A(){}
};

class B: public A
{
};

int main()
try
{
    B b;
    std::cout << type_name_string( static_cast<const A *>( &b ) ) << std::endl;
}
catch( std::exception &e )
{
    std::cout << "Something went wrong: " << e.what() << std::endl;
}
catch( ... )
{
    std::cout << "Something went wrong, no idea what!" << std::endl;
}


Which now always prints "class B."

Personally I'd not bother making type introspection a member of a class - about the only thing making it a class member does is guarentee that you have a virtual function in the base class, which may or may not be what you want. Not all classes need virtual functions so I'd be a bit loathe to stick them in where they're not needed.

Cheers,

Ash

PS: Buy a copy of the "C++ Programming Language" by Stroustrup. He goes over how to use typeid and type_info including the requirement for at least one virtual function. For more "why it works that way in C++" have a gawp at "The Design and Evolution of C++" by the same author.
 
Share this answer
 
What's wrong with

C++
class Base
{
    public:
    const char* GetName() const
    {
        return typeid(*this).name();
    }
};


?
 
Share this answer
 
v2
Comments
CPallini 20-Jun-10 9:28am    
Removed useless 'virtual', thanks John.
I thought already thought about that (putting the function as public member function into the base class), but this does not work.

#include <typeinfo>
#include <iostream>

class Base
{
public:
	const char* GetName() const
	{
		return typeid(*this).name();
	}
};

class Child : public Base {};

int main()
{
	Base* b = new Base();
	Child* c = new Child();

	std::cout << b->GetName() << std::endl;
	std::cout << c->GetName() << std::endl;

	delete c;
	delete b;

	system("pause");
	return 0;
}


The output will allways be "Base" and never "Child".
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900