C++ のtype_info.name()

C++ではRTTIと呼ばれる機構によって,実行時の型情報が取得できる.
これによって,動的に定まるオブジェクトの型を知ることができる.

#include <typeinfo>
#include <stdio.h>

namespace test{
  class A {
  public:
	virtual void m(){}  // 1. 仮想メソッド宣言
  };
  class B : public A {
  public:
	virtual void m(){}  // 1. 仮想メソッド宣言
  };
}

using namespace test;
main(){
    A * a0;

    a0 = new A();
    const std::type_info & id = typeid(*a0);
    printf("%s\n", id.name());

    a0 = new B();
    const std::type_info & id2 = typeid(*a0);
    printf("%s\n", id2.name());
}

こんなコードを書いて,g++4.0で実行してみると

N4test1AE
N4test1BE

という結果がでる.これらはtest::A, test:Bをマングリングしたものである.ちなみに,typeidは多相的な型でないとうまく機能しない.これは実行時型情報が典型的にはオブジェクトの仮想関数テーブルに収められるためである.例えば,上のコードで「仮想メソッド宣言」とコメントのついた2行を削除するとAとBは多相的でなくなるため,結果が変わってしまう.

N4test1AE
N4test1AE

Visual C++では

type_info.name() で型の名前が取得できるが,この値のフォーマットは,仕様で規定されていない.従って,コンパイラ依存となっている.上記のコードをVisual C++ 2008 expressで実行すると,

class test::A
class test::B

となる.したがって,コンパイラが異なるシステムで型情報をやりとりするには,このあたりをちゃんと吸収してやらなければならないわけだ.

マングリングとは

さて,g++で出てくる謎めいた文字列をどうやってデコードしたもんだろう? マングリングに関してはwikipediaにいい記事があった.が,コンパイラに非常に強く依存しているようなので,一筋縄では行かない.

どうせコンパイラに依存しているのであれば,コンパイラの提供しているライブラリを使えばいい,という説も.
こちらの記事によるとg++には__cxa_demangleという関数がありこれをつかえばよいとのこと.

extern "C" char *__cxa_demangle (
     const char *mangled_name,
     char *output_buffer,
     size_t *length,
     int *status);

std::string demangle(const char * name) {
    size_t len = strlen(name) + 256;
    char output_buffer[len];
    int status = 0;
    return std::string(
        __cxa_demangle(name, output_buffer, 
                       &len, &status));
}

こうしておいて,

 a0 = new A();
 const std::type_info & id = typeid(*a0);
 printf("%s\n", demangle(id.name()).c_str());

こうすると

test::A

が得られる.めでたし?