34.1 基本コンセプト
34.1.1 アセンブリから型を取り込む
通常、型がメタデータ中で参照される時、それらは以下の形式で完全に修飾されています。
[ assembly-name ] namespace-name . type-name
例外は C++/CLI 基本型名であり(そして、それは CLIビルトイン型名と同意語です)、そして CLI ビルトイン型名は同意なので直接使われます。
#using <mscorlib.dll> // 冗長
#using <System.dll> // Socket に必要
#using <System.Xml.dll> // XmlTextReader
int main() {
System::Text::StringBuffer^ strBld;
System::Net::Sockets::Socket^ soc;
System::Xml::XmlTextReader^ xtr;
int i; // System::Int32 の同意形で
// int32 と等価です
System::Int64 j; // int64 と等価
System::String^ str; // string と等価
System::Object^ obj; // object と等価
}
.method ... main() ... {
...
.locals ([0] class [mscorlib]System.Text.StringBuilder V_0,
[1] class [System.Xml]System.Xml.XmltextReader V_1,
[2] class [System]System.Net.Sockets.Socket V_2,
[3] int32 V_3,
[4] int64 V_4,
[5] string V_5,
[6] object V_6)
...
}
]
34.2 型
34.2.1 参照型
refクラスやインターフェイス・クラス型への追跡参照はメタデータ中で IsImplicitlyDereferenced(
§33.1.5.5 )modopt を伴う型として発行されるべきです。
値クラスへの追跡参照はメタデータ中でその modopt なしのマネージド(委託)ポインタとして発行されるべきでしょう。[例:
public ref class R {};
public value class V {};
public interface class I {};
void F1(R% tr1) {}
void F2(I% tr2) {}
void F3(V% tr3) {}
void F4(int% tr3) {}
.method assembly static void F1(class R modreq([mscorlib]
System.Runtime.CompilerServices.IsImplicitlyDereferenced) tr1) ... { ... }
.method assembly static void F2(class I modreq([mscorlib]
System.Runtime.CompilerServices.IsImplicitlyDereferenced) tr2) ... { ... }
.method assembly static void F3(valuetype V& tr3) ... { ... }
.method assembly static void F4(int32& tr3) ... { ... }
]
34.2.2 内部ポインタ
型への内部(interior)ポインタはメタデータ中で IsExplicitlyDereferenced(
§33.1.5.4 )modopt を伴う型へのマネージド(委託)ポインタとして発行されるべきです。[例:
public ref class R {};
public value class V {};
public interface class I {};
void F1(interior_ptr<R^> ip1) {}
void F2(interior_ptr<I^> ip2) {}
void F3(interior_ptr<V> ip3) {}
void F4(interior_ptr<int> ip3) {}
.method assembly static void F1a(class R& modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) ip1) … { … }
.method assembly static void F2a(class I& modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) ip2) … { … }
.method assembly static void F3a(valuetype V& modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) ip3) … { … }
.method assembly static void F4a(int32& modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) ip3) … { … }
]
34.2.3 ピンニング・ポインタ
ピンニング・ポインタはメタデータ中で pinned 修飾子と IsExplicitlyDereferenced(
§33.1.5.4 )modopt で発行されます。[例:
value struct V {
int Data;
void F() {
pin_ptr<V> ppv = this;
V* pv = ppv;
}
};
int main() {
V v;
pin_ptr<V> ppv = &v;
int* pi = &ppv->Data;
}
.class … V … {
.field public int32 Data
.method … F() … {
…
.locals ([0] valuetype V& pinned modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) V_0,
[1] valuetype V* V_1)
…
}
}
.method … main() … {
…
.locals ([0] valuetype V& pinned modopt([mscorlib]
System.Runtime.CompilerServices.IsExplicitlyDereferenced) V_0,
[1] int32* V_1,
[2] valuetype V V_2)
…
}
]
34.2.4 ネイティブ配列
ネイティブ配列のエンコーディングはメタデータでは未規定です。
[注意:これはそのような配列は public 可視性を持つことができないため、interop 問題を引き起こしません。]
34.3 変数
34.3.1 ファイル・スコープと名前空間スコープ変数
ファイル・スコープと名前空間スコープの変数宣言と定義のエンコーディングはメタデータ中で未規定です。
[注意:これはそのような宣言と定義は public 視認性を持つことができないため、interop 問題を引き起こしません。]]
34.4 型変換
34.4.1 文字列リテラル変換
<narrow-string-literal-type> ないし <wide-string-literal-type> が System::String^ に変換される時、その結果は CLI 文字列リテラルとして扱われます。[例:
void F(String^ s);
F("red\t" "car\n");
F("ABC\xFF");
F(L"blue");
F(L"\xFF" L"\xFE");
ldstr "red\tcar\n"
call void F(string)
ldstr bytearray (41 00 42 00 43 00 FF 00 )
call void F(string)
ldstr "blue"
call void F(string)
ldstr bytearray (FF 00 FE 00 )
call void F(string)
]
34.4.2 ボックス化変換
ボックス化変換は CLI標準、パーティションIII、§4 で仕様規定されている box 命令を通して成し遂げられます。これは CLI ヒープ上に値クラス・インスタンスの実行時ビット単位コピーを起こします。[例:
int main() {
Console::WriteLine("{0}, {1}", 10, TimeSpan::MinValue);
}
.method … main() … {
ldstr "i = {0}"
ldc.i4.s 10
box [mscorlib]System.Int32
ldsfld valuetype [mscorlib]System.TimeSpan
[mscorlib]System.TimeSpan::MinValue
box [mscorlib]System.TimeSpan
call void [mscorlib]System.Console::WriteLine(string, object, object)
ldc.i4.0
ret
}
]
34.4.3 型変換関数
refクラスでは、暗黙の変換関数は op_Implicit の名前を持ち、明示的変換関数は op_Explicit の名前を持つべきです。
ネイティブ・クラスでは、暗黙の変換関数は <op_Implicit> の名前を持ち、明示的変換関数は <op_Explicit> の名前を持つべきです。
全ての変換関数は specialname のマークをされているべきでしょう。op_Implicit と op_Explicit はその返却型を多重定義することができます。[例:
public value struct Decimal {
…
static operator Decimal(int value);
static explicit operator double(Decimal value);
explicit operator float();
};
.class public sequential … Decimal … {
.method public specialname static valuetype Decimal op_Implicit(
int32 value) … { … }
.method public specialname static float64 op_Explicit(
valuetype Decimal value) … { … }
.method public specialname instance float32 op_Explicit()
… { … }
}
]
コンストラクタの変換は決して変換関数としてではなく、コンストラクタとして発生します。(refクラスと値クラスのコンストラクタは常に明示的です。)
34.5 演算
34.5.1 クラス・メンバ・アクセス
基底クラス(それは System::ValueType か System::Object のみなることができる)の仮想関数を呼ぶ値型のインスタンスを使って、その値型は自身でその関数を上書きしない場合、値型のインスタンスはボックス化されるべきです。値型のメンバにアクセスする他のどんな場合でもボックス化を引き起こすことはありません。[例:
value struct V {
virtual int GetHashCode() override { … }
};
int main() {
V v;
… = v.GetHashCode(); // V::GetHashCode を呼び出し
… = v.ToString(); // ValueType::ToString を呼び出し
}
.method … main() … {
…
.locals ([0] valuetype V V_0)
ldloca.s V_0
initobj V
ldloca.s V_0
call instance int32 V::GetHashCode()
…
ldloc.0
box V
callvirt instance string [mscorlib]System.ValueType::ToString()
…
}
V は GetHashCode を上書きするので、call 命令の前に box 命令を行う必要はありません。しかしながら、V は ToString を上書きしないため、 ValueType の版の ToString が使われ、結果として callvirt 命令によって付き従われた box 命令が使われます。
]
34.5.2 ダイナミック・キャスト
キャストに実行時チェックが宛われ、T が CLI クラス型への参照である場合、実行時チェックは isinst 命令を使うことで実行されるべきでしょう。
34.5.3 セーフ・キャスト
"cv2 B へのハンドル"が" cv1 D へのハンドル"に型変換される時、D が B から継承するかどうか castclass 命令によって実行時チェックが実行されます。変換の結果はその命令の結果です。
"cv2 B" が "cv1 D の追跡参照"に型変換される時、D が B から継承するかどうか castclass 命令によって実行時チェックが実行されます。変換の結果は castclass の結果を参照外ししたものです。
" cv1 R へのハンドル"型の右辺値が V 型の左辺値に変換される時、 unbox 命令が使われます。
34.6 関数
34.6.1 名前検索
入力において、メタデータ中の hidebysig 記述の存在、ないし、不在は無視されます。全てのネイティブ型は hidebyname メンバを持っているものとして扱われますし、全ての CLI クラス型は hidebysig メンバを持っているものと扱われるからです。
[注意:出力において、CLI クラス型はそのメンバ各々に hidebysig(
§34.7.4 )マークを持つべきです。]
34.6.2 パラメータ配列
関数はその最後のパラメータとしてパラメータ配列を持つことができます。そのようなパラメータは .custom 指定子中の標準属性 System::ParamArrayAttribute が、その関数のために生成された .method 指定子中の最後のパラメータ、という結果になるべきです。[例:
void f(... array<Object^>^ p) { … }
int main() {
array<Object^>^ a1 = gcnew array<Object^>(2);
array<Object^>^ a2 = gcnew array<Object^>(4);
array<Object^>^ a3 = gcnew array<Object^>(8);
f(a1);
f(a2, a1);
f(a1, a3, a2);
}
.method assembly static void f(object[] p) … {
.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor()
= ( 01 00 00 00 )
…
}
]
パラメータ配列を取る関数の最後のパラメータは与えられた型の配列へのハンドルです。そのような関数の呼び出しは、その構文上の順番で関数に与えられた引数で初期化されたその配列の連続要素で、与えられた型の配列の領域を確保したものに変換されるべきでしょう。
[例:ここにメンバ関数にパラメータ配列を使う物の例を提示します。
public ref struct C {
static void F(int val, ... array<String^>^ list) { … }
static void TestF() {
F(10, "red", "blue", "green");
}
};
.class public … C … {
.method public static void F(int32 val, string[] list) … {
.param [2]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor()
= ( 01 00 00 00 )
}
.method public static void TestF() … {
.maxstack 3
.locals (string[] V_0)
ldc.i4.3
newarr [mscorlib]System.String
stloc.0
ldloc.0
ldc.i4.0
ldstr "red"
stelem.ref
ldloc.0
ldc.i4.1
ldstr "blue"
stelem.ref
ldloc.0
ldc.i4.2
ldstr "green"
stelem.ref
ldc.i4.s 10
ldloc.0
call void C::F(int32, string[])
ret
}
}
]
34.6.3 ネイティブ関数の取り込み
もし、関数が(名前空間 System::Runtime::InteropServices 中の)属性 DllImportAttribute を持つ場合、コンパイラはメタデータ中にカスタム属性としてその型を保護しないことを要請されます。その代わり、コンパイラはそれを直接ファイルフォーマットに発行するべきです。(そのようなメタデータの消費者はこのデータをファイル・フォーマットから受け取り、それがカスタム属性であるかのように返すことを要求されます。)
生成された .method 指定子は定義済み属性 pinvokeimpl によってマークされるべきです。その最初のクォート文字列は関数の実装が配置される場所を示すプラットフォーム依存記述です。そして、オプションとなる二番目の文字列はそのプラットフォーム上に存在する関数の名前です。メソッドの本体は空であるべきです。[例:
// MyCLib.h
using namespace System::Runtime::InteropServices;
[DllImport("MyCLib.dll", CallingConvention =
CallingConvention::StdCall, EntryPoint="Hypot" )]
extern "C" double Hypotenuse(double s1, double s2);
.method public static pinvokeimpl("MyCLib.dll" as "Hypot" stdcall)
float64 Hypotenuse(float64 s1, float64 s2) cil managed {}
}
// MyCLibApp.cpp
#include "MyCLib.h"
int main() {
Console::WriteLine("Hypotenuse = {0}", Hypotenuse(3, 4));
}
.method … main() … {
ldstr "Hypotenuse = {0}"
ldc.r8 3.
ldc.r8 4.
call float64 Hypotenuse(float64, float64)
box [mscorlib]System.Double
call void [mscorlib]System.Console::WriteLine(string, object)
ldc.i4.0
ret
}
]
もし、関数パラメータか返値が(名前空間 System::Runtime::InteropServices中の)属性 MarshalAsAttribute を持っているなら、コンパイラはメタデータ中の型はカスタム属性として保護されないことを要請されます。その代わり、コンパイラはそれを直接ファイルフォーマットに発行するべきです。(そのようなメタデータの消費者はこのデータをファイル・フォーマットから受け取り、それがカスタム属性であるかのように返すことを要求されます。)生成された .method 指定子中のパラメータや返値は UnManagedType 引数を渡されることによって marshal 属性でマークされるべきです。[例:
using namespace System::Runtime::InteropServices;
[DllImport("msvcrt.dll", CallingConvention = CallingConvention::Cdecl)]
extern "C" int strcmp([MarshalAs(UnmanagedType::LPStr)] System::String^ s1,
[MarshalAs(UnmanagedType::LPStr)] System::String^ s2);
.method public static pinvokeimpl("msvcrt.dll" cdecl)
int32 strcmp(string marshal(lpstr) s1, string marshal(lpstr) s2)
cil managed {}
]
34.6.4 非メンバ関数
メタデータ中の非メンバ関数のエンコーディングは未規定です。[注意:これはそのような関数は public 可視性を持てないために、interop 問題を引き起こしません。]