名前修飾

名前修飾（なまえしゅうしょく、name mangling）は、現代的なコンピュータプログラミング言語処理系で用いられている手法で、サブルーチン（関数）名などに対する内部名を、その表層的な名前だけではなく、関数であればその引数の型や返戻値の型などといった意味的な情報を含めて修飾した（manglingした）名前とするものである。コンパイラからリンカ、さらには実行時のデバッガなども含んだシステム全体が、高度な型に関する情報などをサポートするように再実装するには多くの難しさがあるが、この手法であれば、システムの多くの部分ではわずかな修正（たとえば、名前に '$' という文字が含まれることを許すようにする、など）で済む。特に、多重定義を許す言語では、同一の表層名に対して許される多重定義や、その同定について上手に修飾を設計すれば、扱いが単純になる。また、そのままではエラーメッセージ等が読み辛いものとなるが、「解読」ルーチンを呼ぶように修正するだけで、型の情報などが付加された、むしろわかりやすいメッセージが出力されるようになる。

Microsoft Windowsの場合

一般的なC・Pascalなどの言語は関数の多重定義をサポートせず、名前修飾を必要としないが、場合によっては名前修飾によって関数についての情報を付加することがある。
例えば、Microsoft Windows 上のコンパイラは複数の呼出規約（サブルーチンとデータをやりとりする方法）をサポートしている。呼出規約の間には互換性がないので、コンパイラは名前修飾によって呼出規約を詳細に記述する。
MSによって確立された名前修飾のスキームがあり、非公式に他のコンパイラもこれに従っている。例えば、Digital Mars・ボーランド・gccである。このスキームは他の言語、例えば、Pascal・D言語・Delphi・FORTRAN・C#にも適用される。このようにして、それら処理系のデフォルトの呼出規約が異なる場合も、それら処理系で作成したサブルーチンが現存のWindows ライブラリを呼んだり、そこから呼ばれたりすることができる。
次のCコードをコンパイルするとしよう:
int _cdecl    f(int x) { return 0; }int _stdcall  g(int y) { return 0; }int _fastcall h(int z) { return 0; }
_cdecl はCの標準の呼び出し規約を使うことを明示する修飾子である。
32 bit コンパイラはそれぞれ、以下を出力する。
_f
_g@4
@h@4
stdcallとfastcallでは、関数名は_名前@X と @名前@Xのようにエンコードされる。Xにはコールスタック（以下単にスタック）に積まれる引数のバイト数が入る。
他の一般的な修飾法は、前置句 (prefix) を、下線付きで（__func__のように）あるいは標準的な大文字化を行ったりして加えるものである。

C++の場合

名前修飾を行う処理系のうち、C++コンパイラは最も広く用いられているが、最も標準化が進んでいないものである。最初のC++コンパイラはCソースコードへのトランスレータとして実装された。そのため、シンボルの名前はCの識別子の規則に従う必要があった。後にC++コンパイラ自身が機械語コードやアセンブラコードを出力するようになっても、計算機システムのリンカは総じてC++のシンボルをサポートせず、名前修飾が必要な状態が続いた。
C++言語は、標準的な修飾規則を定めていない。そこで、コンパイラによって修飾規則が異なる。C++の修飾がかなり複雑になりうること（クラス、デフォルトの引数、変数のオーナー、演算子オーヴァーロードなどの情報を格納する）も加わり、異なるコンパイラのオブジェクトコードはリンクすることができないのが通常である。

簡単な例

C++におけるf()の、次の二つの定義をみてみよう:
int f (void) { return 1; }int f (int)  { return 0; }void g (void) { int i = f(), j = f(0); }
この二つは異なった関数である。名前以外に全く関係がない。馬鹿正直にこれをCに変換すると、Cコンパイラはエラーを吐く — 関数の名前の重複は許されないからである。そこで、C++コンパイラはシンボル名に型情報を加える。例えばこんな風になるだろう:
int __f_v (void) { return 1; }int __f_i (int)  { return 0; }void __g_v (void) { int i = __f_v(), j = __f_i(0); }
ここで、g()は名前の重複問題がないのに修飾されていることに注意して欲しい。修飾は全てのシンボルに適用される。

複雑な例

もっと複雑な例を挙げる。実際に用いられている名前修飾を見てみよう。GNU GCC 3.xは次のクラス例をどのように修飾するだろうか。修飾されたシンボルはそれぞれの識別子の下に表示されている。
namespace wikipedia {
   class article {
   public:
      std::string format (void); 
                ''/* = _ZN9wikipedia7article6formatEv */''

      bool print_to (std::ostream&); 
                ''/* = _ZN9wikipedia7article8print_toERSo */''

      class wikilink {
         public:
            wikilink (std::string const& name);
                    ''/* = _ZN9wikipedia7article8wikilinkC1ERKSs */''
      };
   };}
ここでの名前修飾スキームは比較的単純である。修飾された名前は全て _Z で始まる。下線に大文字を続けたものはCおよびC++では予約語であることに注意されたい。従って、ユーザの識別子とぶつかることはない。ネストされた名前は（名前空間、クラス両者）、Nをつけ、次いで <長さ,id> のペアをつけて示す（「長さ」は次の識別子の長さ）。最後にEを付ける。例えば、wikipedia::article::formatは
_ZN·9wikipedia·7article·6format·E  
となる。
関数の場合は、続いて型情報が付加される。format()は void 関数なので、単にvを付ける。よって、
_ZN·9wikipedia·7article·6format·E·v
となる。
print_toの場合は、標準的な型として std::ostream （あるいは、もっと正確には std::basic_ostream<char, char_traits<char> >）が用いられ、これには特殊な別名 So がある。よって、この型に対する参照はRSoとなる。名前の完成形はこうなる。
_ZN·9wikipedia·7article·8print_to·E·RSo

C++からリンクする際の C シンボルの扱い

次のようなよくあるC++の例
#ifdef __cplusplus extern "C" {#endif
    /* ... */#ifdef __cplusplus}#endif
は、引き続くシンボルを修飾しないことを指示する。すなわち、コンパイラはあたかもCコンパイラであるかのように、修飾なしの名前を用いたバイナリを吐く。Cが名前修飾を利用していないので、C++コンパイラもそれらの識別子を参照する際名前修飾を避けなければならない。
例として、標準的な文字列ライブラリ <string.h> は、通常次のようなコードを含む:
#ifdef __cplusplusextern "C" {#endifvoid *memset (void *, int, size_t);char *strcat (char *, const char *);int   strcmp (const char *, const char *);char *strcpy (char *, const char *);#ifdef __cplusplus}#endif
そこで、次のコード
if (strcmp(argv[1], "-x") == 0) 
    strcpy(a, argv[2]);else 
    memset(a, 0, sizeof(a));
は、正しい、修飾されない strcmp 及び memset を用いることになる。extern が用いられなければ、C++コンパイラは同等の次のコードを生成するだろう
 if (__1cGstrcmp6Fpkc1_i_(argv[1], "-x") == 0) 
     __1cGstrcpy6Fpcpkc_0_(a, argv[2]);
 else 
     __1cGmemset6FpviI_0_(a, 0, sizeof(a));
これらのシンボルはCのランタイムライブラリ（例えば libc）には存在しないので、リンカはエラーを報告することになる。

C++での名前修飾の標準化

C++で名前修飾の標準化を行うと、実装をまたいだ運用がしやすくなるというのが比較的広く信じられているが、これは実際には正しくない。名前修飾はApplication Binary Interface (ABI) や他の細かな言語仕様（例外処理、仮想テーブルのレイアウト、構造体のパディングなど）におけるいくつかの問題の一つに過ぎず、名前修飾だけをどうかしても非互換性は残ることになる。更に、特定の修飾法を決めてしまうと、実装が制限されるシステムが出現しうる（例えば、シンボルの長さ）。また、名前修飾を標準化してしまうと、例えばC++の文法を理解できるリンカのような、名前修飾を必要としない実装を妨げる可能性もある。
そのため、ISOではC++の標準 (en:ISO/IEC 14882) として、名前修飾を標準化することを特に目指してはいない。逆に、Annotated C++ Reference Manual（ARM の名でも知られる。 ISBN 0-201-51459-1, section 7.2.1c）では、ABI上の他の非互換性を抱えたモジュールを誤ってリンクしないように、異なった名前修飾法を用いることが推奨されている。

C++名前修飾問題の現実的な影響

C++のシンボルはDLLや共有オブジェクトを通してルーチン的にエクスポートされるため、名前修飾スキームはコンパイラの問題だけではすまなくなる。ライブラリをコンパイルするにあたって、複数のコンパイラ（場合によっては、同じコンパイラでも版が異なるだけで問題になりうる）によって名前修飾がそれぞれ異なったスキームで行われると、それらのライブラリを参照する際、しばしばシンボルが解決できなくなってしまう。例えば、複数のC++コンパイラ（例えば、GCCとOS付属のコンパイラ）が導入されているシステムにBoostを導入しようとすると、二度それをコンパイルしなければならない ―― OSベンダのコンパイラとGCCで。
このため、名前修飾はC++が関係した ABIでの重要な側面の一つとなっている。

Java の場合

言語、コンパイラ、.classファイルフォーマットが同時に設計され、また開発当初からオブジェクト指向が取り入れられていたため、名前修飾を必要とするような問題はJava実行時環境の実装には存在しない。しかしながら、これまでに見てきた名前修飾に類似した名前の変換が必要な場合がある。

Java - ネイティブ間のインターフェースの扱い

Javaのネイティブメソッドサポートによって、Javaで記述されたプログラムから他の言語で書かれたプログラム（通常CまたはC++）を呼ぶことができる。ここでは二つの名前解決が関係するが、'Java Native Interface(JNI)として標準化されている。これとは別に、Java Native Access(JNA)とは、Javaプログラムからネイティブの共有ライブラリにアクセスする方法をライブラリレベルで提供する。

Python の場合

Pythonのプログラマは識別子の最初の二文字を下線にすることで、明示的にそれが「プライベートな名前」（スコープがクラスに限られる）であることを示すことができる。コンパイラはこれらを見るとプライベートな名前を大域的なシンボルに置き換える。これは、その識別子を包含するクラスの名前を一個の下線とともに前づけして行われる。例えば、
class Test:
    def __privateSymbol(self):
        pass
    def normalSymbol(self):
        passprint dir(Test)
はつぎのようになる
 ['_Test__privateSymbol', 
 '__doc__', 
 '__module__', 
 'normalSymbol']

Turbo Pascal / Delphi の場合

これらのPascal処理系では、次のようにして名前修飾を抑制する。
exports
  myFunc name 'myFunc', myProc name 'myProc';

Objective-Cの場合

Objective-Cのメソッドは、本質的に二種類に分けられる。一つは クラス（「静的」）メソッドで、もう一つはインスタンスメソッドである。Objective-Cでのメソッド宣言は次のような形式である。
+ method name: argument name₁:parameter₁ ...
- method name: argument name₁:parameter₁ ...
クラスメソッドは + で示される。インスタンスメソッドは - で示される。典型的なクラスメソッド宣言は、次のようになるだろう
+ (id) initWithX: (int) number andY: (int) number;
+ (id) new;
インスタンスメソッドならば、次のようである
- (id) value;
- (id) setValue: (id) new_value;
それぞれのメソッド宣言は特有の内部表現を持っている。コンパイル時に、メソッド名は次のスキームによって変換される。クラスメソッドでは
_c_Class_methodname_name₁_name₂_ ...
となり、インスタンスメソッドでは
_i_Class_methodname_name₁_name₂_ ...
となる。
Objective-Cのコロンは下線に変換される。そこで、Point クラスに属するクラスメソッド  + (id) initWithX: (int) number andY: (int) number; は次のように変換されるだろう_c_Point_initWithX_andY_。同じクラスに属するインスタンスメソッド  - (id) value; は _i_Point_value となる。
クラスの各メソッドはこのようにラベルされるが、全てのメソッドがこのように表現された場合、あるクラスが応答すべきメソッドを探し出すのは面倒な作業となりうる。そのため、各々のメソッドに整数のようなシンボルを一意に割り当てる。このようなシンボルは「セレクタ」として知られる。Objective-Cでは、プログラマがセレクタを直接管理することができる — Objective-Cではそれらに特別の型を与えている — SEL。
コンパイル中に、（_i_Point_valueのような）文字による表現からセレクタ（SEL型）へのマップが作成される。文字による表現を操作するよりもセレクタを管理する方がメソッドを効果的に扱うことができる。セレクタがマッチするのはメソッドの名前だけであり、それが属するクラスではないということに注意してほしい。クラスが異なれば同じ名前のメソッドでも実装が異なることがある。このため、メソッドの実装にも特別の識別子が与えられる — 実装ポインタ (implementation pointer) と呼ばれ、IMP 型を持つ。
オブジェクトにメッセージを送ると、それはコンパイラによって、 id objc_msgSend(id receiver, SEL selector, ...) 関数ないしはその従兄弟のどれかに対する呼び出しとしてエンコードされる。ここで、receiverはそのメッセージの受け手であり、SELによって呼び出されるメソッドが決まる。各々のクラスはそれ自身の表を持っており、セレクタと実装 — メソッドの実体が存在するメモリ空間を指定する実装ポインタ — との相互対照ができるようになっている。また別の表にはクラスとインスタンスメソッドが記録される。SELからIMPへの対照表に格納されることはさておき、関数は本質的に無名である。
あるセレクタに対するSELの値はクラスによって変わることがなく、多態性を実現している。
Objective-Cの実行環境はメソッドの引数と返り値の型についての情報を保持しているが、メソッドの名前の一部として保持されるわけではなく、クラスによって変化しうる。
Objective-Cは名前空間をサポートしないので、クラス名を修飾する必要はない。