By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,402 Members | 1,216 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,402 IT Pros & Developers. It's quick & easy.

typeid(T).nameOfTheBeast()

P: n/a
Sometimes, in an introspective mood, my code reflecting on itself, I like to
write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)), returns
an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

returns "j". And while I do appreciate the savings involved it nonetheless
irritates readers of the output - rather than "j" they would like to see
"unsigned int". Bother!

So I had a thought:

template<typename T>
struct TName
{
static const char* Is()
{
return typeid(T).name();
}
};

template<>
struct TName<bool>
{
static const char* Is() { return "bool"; }
};

....

template<>
struct TName<long double>
{
static const char* Is() { return "long double"; }
};

And those homegrown Types that want to play nice do this:

template<> struct TName<AClass>
{ static const char* Is() { return "AClass"; } };

Any better ideas?
Jul 23 '05 #1
Share this Question
Share on Google+
17 Replies


P: n/a
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like to
write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)), returns
an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

Why not just use overloaded functions, e.g.

const char* is (bool b) { return "bool";}
const char* is (const long double& ld) { return "long double";}

template <typename T>
const char* is (const T& t) { return typeid(T).name();}
void foo() {
bool test;
cout << "current type is " << is(test) << endl;
cout << "current type is " << is(std::string()) << endl;
}

Jul 23 '05 #2

P: n/a
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like
to write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)),
returns an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

returns "j". And while I do appreciate the savings involved it nonetheless
irritates readers of the output - rather than "j" they would like to see
"unsigned int". Bother!


Well, if you can live with using compiler specific functionality here, then
you could demangle that name. g++ uses just the same string that would be
used in name mangling. Look into the cxxabi.h header. It provides a
function abi::__cxa_demangle() that can convert the name returned by
typeid(T).name() into what you want.

xpost&f'up2 gnu.g++.help, because that group is clearly more appropriate for
answers to my posting.

Jul 23 '05 #3

P: n/a
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like to
write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)), returns
an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

returns "j". And while I do appreciate the savings involved it nonetheless
irritates readers of the output - rather than "j" they would like to see
"unsigned int". Bother!


The GNU utility c++filt, which demangles symbol names, is a wrapper
around a library function from binutils (libbfd?) whose prototype I
can't immediately find in /usr/include. It takes mangled symbol names
like _Z23frobnicateRK213_fooBlah and turns them into `string
frobnicate(unsigned int, struct foo)'; I believe that it is doing what
you need to get the parameter types. However I don't recall it having a
public interface for just doing the type demangling.

grepping in /usr/include has revealed something called __cxa_demangle in
/usr/include/c++/3.3/cxxabi.h. Not sure how that is related.

If all else fails I'm sure the binutils source, grepped for mangl, will
reveal the answer.

--Phil.
Jul 23 '05 #4

P: n/a
ben
> Why not just use overloaded functions, e.g.

because you need an instance of the type to pass as parameter, consider:

class shape
{
public:
virtual draw() = 0;
};

const char* is (shape a); // illegal

ben

const char* is (bool b) { return "bool";}
const char* is (const long double& ld) { return "long double";}

template <typename T>
const char* is (const T& t) { return typeid(T).name();}
void foo() {
bool test;
cout << "current type is " << is(test) << endl;
cout << "current type is " << is(std::string()) << endl;
}

Jul 23 '05 #5

P: n/a
Phil Endecott wrote:
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like
to write test code that prints the results of introspection, some of
which consist of the names of Types. The problem is: typeid(T).name(), in
my usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)),
returns an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

returns "j". And while I do appreciate the savings involved it
nonetheless irritates readers of the output - rather than "j" they would
like to see "unsigned int". Bother!


The GNU utility c++filt, which demangles symbol names, is a wrapper
around a library function from binutils (libbfd?) whose prototype I
can't immediately find in /usr/include. It takes mangled symbol names
like _Z23frobnicateRK213_fooBlah and turns them into `string
frobnicate(unsigned int, struct foo)'; I believe that it is doing what
you need to get the parameter types. However I don't recall it having a
public interface for just doing the type demangling.

grepping in /usr/include has revealed something called __cxa_demangle in
/usr/include/c++/3.3/cxxabi.h. Not sure how that is related.

If all else fails I'm sure the binutils source, grepped for mangl, will
reveal the answer.

--Phil.


I find this to be a fairly telling observation regarding where introspection
can, in general, be useful. This is by no means an exclusive rule, but my
observation is that introspection is often most useful at
design/coding/debugging time, rather than production runtime. I would like
to see a generic interface that hides compiler specific idiosyncracies, and
provides the kind of introspection and reflection that's useful to the
programmer, and tool developer in the way Java's javap command is for that
language. I'd even begrudgingly entertain the notion that a bit of #MACRO
magic might be useful in this respect.
--
If our hypothesis is about anything and not about some one or more
particular things, then our deductions constitute mathematics. Thus
mathematics may be defined as the subject in which we never know what we
are talking about, nor whether what we are saying is true.-Bertrand Russell
Jul 23 '05 #6

P: n/a
ben wrote:
Why not just use overloaded functions, e.g.
because you need an instance of the type to pass as parameter, consider:


which is a feature, not a liability. You don't need to type the actual
type.
class shape
{
public:
virtual draw() = 0;
};

const char* is (shape a); // illegal


What's your point? Do you want something like this?

#define IS(type) (#type)
cout << "current type is " << IS(shape) << endl;

Jul 23 '05 #7

P: n/a
ben
> I would like
to see a generic interface that hides compiler specific idiosyncracies, and provides the kind of introspection and reflection that's useful to the
programmer, and tool developer in the way Java's javap command is for that
language. I'd even begrudgingly entertain the notion that a bit of #MACRO
magic might be useful in this respect.


I really think and hope that the static type reflection mechanism, which
does exactly what the OP requested, is going to be part of the C++0x
standard. But since that will take AGES AND AGES (2009?) to happen and then
AGES AND AGES(2015?) for the compiler vendors to adopt, we will have to hand
write a lot of template specialization before that. And yes, #MACRO helps!

ben
Jul 23 '05 #8

P: n/a
ben
> which is a feature, not a liability. You don't need to type the actual
type.
class shape
{
public:
virtual draw() = 0;
};

const char* is (shape a); // illegal


What's your point? Do you want something like this?

#define IS(type) (#type)
cout << "current type is " << IS(shape) << endl;


What I want, what the OP probably wants, is a typeid() operator without any
name mangling. A workaround would be something like:

cout << type_name<shape>::is(); // prints "shape"

And a helper function can be easily provided to take objects instead of
types:

template <typename T>
char* type_name_of(const T& instance)
{
return type_name<T>::is();
}

shape* s = new circle;
cout << "current type is " << type_name_of(*s);
delete s;

ben
Jul 23 '05 #9

P: n/a
ben
> which is a feature, not a liability. You don't need to type the actual
type.
class shape
{
public:
virtual draw() = 0;
};

const char* is (shape a); // illegal
What's your point? Do you want something like this?

#define IS(type) (#type)
cout << "current type is " << IS(shape) << endl;


What I want, what the OP probably wants, is a typeid() operator without any
name mangling. A workaround would be something like:

cout << type_name<shape>::is(); // prints "shape"

And a helper function can be easily provided to take objects instead of
types:

template <typename T>
char* type_name_of(const T& instance)
{
return type_name<T>::is();
}

shape* s = new circle;
cout << "current type is " << type_name_of(*s);
delete s;

ben
"Panjandrum" <pa********@spambob.com> wrote in message
news:11*********************@f14g2000cwb.googlegro ups.com... ben wrote:
Why not just use overloaded functions, e.g.


because you need an instance of the type to pass as parameter, consider:


which is a feature, not a liability. You don't need to type the actual
type.
class shape
{
public:
virtual draw() = 0;
};

const char* is (shape a); // illegal


What's your point? Do you want something like this?

#define IS(type) (#type)
cout << "current type is " << IS(shape) << endl;

Jul 23 '05 #10

P: n/a

"Panjandrum" <pa********@spambob.com> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like
to
write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)),
returns
an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

Why not just use overloaded functions, e.g.

const char* is (bool b) { return "bool";}
const char* is (const long double& ld) { return "long double";}

template <typename T>
const char* is (const T& t) { return typeid(T).name();}


For one thing, functions cannot be partially specialized.

template<typename T>
struct TName<std::vector<T> >
{
static const char* Is()
{
static std::string namestring =
"std::vector<" + std::string(TName<T>::Is()) + ">";
return namestring.c_str();
}
};
Jul 23 '05 #11

P: n/a
ben
> template <typename T>
char* type_name_of(const T& instance)
{
return type_name<T>::is();
}

shape* s = new circle;
cout << "current type is " << type_name_of(*s);
delete s;


Oops! Sorry! This still prints "shape" instead of "circle". Obviously, some
runtime operation has to perform to trace to the most derived type...which
is what typeid does. For objects instead of pointers the template works
fine:

circle c;
cout << "current type is " << type_name_of(c); // prints "circle"

ben
Jul 23 '05 #12

P: n/a
ben wrote:
[...] I'd even begrudgingly entertain the notion that a bit of
#MACRO magic might be useful in this respect.


I really think and hope that the static type reflection mechanism, which
does exactly what the OP requested, is going to be part of the C++0x
standard. But since that will take AGES AND AGES (2009?) to happen and
then AGES AND AGES(2015?) for the compiler vendors to adopt, we will have
to hand write a lot of template specialization before that. And yes,
#MACRO helps!

ben


What is #MACRO? Where can I read about it? Or is it just a generic notation
for preprocessor foo? (I did find the Monopole Astrophysics and Cosmic Ray
Observatory, but somehow I doubt that's what y'all are talking about.)
Jul 23 '05 #13

P: n/a
ben
> What is #MACRO? Where can I read about it? Or is it just a generic
notation
for preprocessor foo? (I did find the Monopole Astrophysics and Cosmic Ray
Observatory, but somehow I doubt that's what y'all are talking about.)


LOL! Yes, it is just a preprocessor foo.
ben
Jul 23 '05 #14

P: n/a

Panjandrum wrote:
Michael Olea wrote:
Sometimes, in an introspective mood, my code reflecting on itself, I like to
write test code that prints the results of introspection, some of which
consist of the names of Types. The problem is: typeid(T).name(), in my
usual worlds (e.g. gcc version 3.2.2 [FreeBSD] 20030205 (release)), returns
an illegible mangling or a cryptic abbreviation. For example:

typeid(unsigned int).name()

Why not just use overloaded functions, e.g.

const char* is (bool b) { return "bool";}
const char* is (const long double& ld) { return "long double";}

template <typename T>
const char* is (const T& t) { return typeid(T).name();}


The disadvantage of using overloaded functions to identify a type is
that the compiler is free to promote or convert the type passed in the
function call in order to match the signature of an overloaded
function, if necessary. For example, if is(int) were declared but not
is(bool), then is(int) would be called for any variable of type bool.
Examining the output of the program would not be enough to indicate
that bools are being reported as ints; it may just be that no bools are
being asked to report their type.

The advantage of the template approach is that there is no type
conversion applied for a parameterized type. TName<int> will never
match a TName<bool>. So if TName<bool> is not declared then the
non-specialized TName class template is applied; the output for a bool
type would be "B" (or whatever). It would not be an "int." Examining
this output, the presence of the "B" types makes clear that there is a
type that needs a specialized TName template.

Greg

Jul 23 '05 #15

P: n/a
Greg wrote:
The advantage of the template approach is that there is no type
conversion applied for a parameterized type. TName<int> will never
match a TName<bool>. So if TName<bool> is not declared then the
non-specialized TName class template is applied; the output for a bool
type would be "B" (or whatever). It would not be an "int." Examining
this output, the presence of the "B" types makes clear that there is a
type that needs a specialized TName template.


Question: Why would someone want to write
const char* s = TName<bool>::Is();

instead of
const char* s = "bool"

Jul 23 '05 #16

P: n/a
ben


Question: Why would someone want to write
const char* s = TName<bool>::Is();

instead of
const char* s = "bool"


If you have a lot of typedef's its sometimes good to figure out what the
underlying type is.
If you are writing templates and want to generate some debugging information
it is good to know the type name.

ben
Jul 23 '05 #17

P: n/a

Panjandrum wrote:
Greg wrote:
The advantage of the template approach is that there is no type
conversion applied for a parameterized type. TName<int> will never
match a TName<bool>. So if TName<bool> is not declared then the
non-specialized TName class template is applied; the output for a bool
type would be "B" (or whatever). It would not be an "int." Examining
this output, the presence of the "B" types makes clear that there is a
type that needs a specialized TName template.


Question: Why would someone want to write
const char* s = TName<bool>::Is();

instead of
const char* s = "bool"


If the goal were to declare a const char *s initialized with a pointer
to the string "bool" then the TName class template would not be all
that useful. But in those situations in which the type being reported
is not hard-coded, then the value of declaring a TName class becomes
more apparent.

For example, take a simple function template:

void MyFunction( T a )
{
std::cout << "MyFunction instatiated with type: " << TName<T>::Is()
<< " and value: " << a << std::endl;
....
}

If MyFunction accepted only a bool parameter than TName would not be
needed to report a's type. But since a's type is not hardcoded, and
could be any kind of type both now and in the future, then TName's
ability to select the correct name based on a's actual type is very
useful in reporting what MyFunction is doing.

Greg

Jul 23 '05 #18

This discussion thread is closed

Replies have been disabled for this discussion.