Home > C++/CLI Archive

C++/CLI Archive

仮想関数を generic にする

C++では、メンバ関数テンプレートを仮想関数にできない。ところが、C++/CLI ではテンプレートに良く似た機能である generic を仮想関数につけることができる。これにはどんなトリックが隠されているのだろう?

まずは、次のようなコードから。


public ref class Base // ref class なので参照型
{
public:
  generic <typename T> // generic を仮想関数につける
  virtual int Func(T in) { // T は任意の型
      return sizeof(in); // T 型のサイズを返す(本当は size_t だけど)
  }
};

public ref class Derived: public Base
{
public:
  generic <typename T>
  virtual int Func(T in) override { // Base::Func を override
      return sizeof(in) * 2; // T 型のサイズを倍にして返す
  }
};

これはTがどんな型であっても正しく動作する。Derived のインスタンスを Base のハンドルで参照しても、Derived 側の Func が呼ばれる。完璧だ。

C++ では、Tの型が決まらないと仮想関数テーブルを構築できず、Tの型を知るためには全ての翻訳単位を調べなければならない。よって、このようなメンバ関数は禁止されている。そして、以前のエントリでは「なんだ、その都度 JIT コンパイルすればいいじゃん」という結論で終わっていた。今回は、これをもう少し調べてみようと思う。

続きを読む

コレクションの永続化

.NET Framework で独自のコントロールを作りたい場合は、UserControl から派生させたクラスを用意すればよい。一般的に、自作コントロールには独自のプロパティも含まれており、そのプロパティが自作クラスになっていることも多いはず。今回は、自作クラスの配列をプロパティとして持たせたところ大ハマリしたという話。

ある自作コントロールに自作クラス MyItem の配列をプロパティとして持たせたいと思い、System::Collections::Generic::List<MyItem^>^ 型のプロパティ MyItemList を追加することにした。cli::array ではなく List を使っているのは vector っぽく使いたいためだが、この違いは本質ではない(と思う)。悲劇の始まりは次のようなコードからだ。


ref class MyItem;
ref class MyCtrl : public System::Windows::Forms::UserControl
{
// ... 色々あって、

private:
  System::Collections::Generic::List<MyItem^>^ m_itemList; // リストの実体

public:
  MyCtrl(void) {
      InitializeComponent();

      // コンストラクタで m_itemList を確保する
      m_itemList = gcnew System::Collections::Generic::List<MyItem^>;
  }

public:
  property System::Collections::Generic::List<MyItem^>^ MyItemList { // このプロパティは
      System::Collections::Generic::List<MyItem^>^ get() { // get だけしか持っていない(set は許可しない)
          return m_itemList; // 中身は自由にしても構わないが、リストそのものはコチラで管理させてくれ
      }
  }

// ... 色々あるよね
};

続きを読む

値型とボックス化

  • Posted by: zakio
  • 2009年3月 4日 13:24
  • C++/CLI

前回の続き。

CLI は値型と参照型を区別する。一般に値型はスタック、参照型はヒープ上に配置されるが、C++/CLI では参照型であっても C++ と同じようにスタックに置くことができる。正確には「置いたように見せかける」だが、構文的にも意味的にもスタックに置いたオブジェクトと同じように扱える。


List<int> list1; // 参照型なのにスタックに置ける(ように見える)
List<int>* list2 = &list1; // コンパイルエラー: CLI heap 上にあるオブジェクトはポインタで参照できない
List<int>& list3 = list1; // コンパイルエラー: CLI heap 上にあるオブジェクトはトラッキング参照を用いること
List<int>^ list4 = %list1; // % でハンドルを取得
List<int>% list5 = list1; // トラッキング参照は & と同様の振る舞い

構文としてはスタックだが、参照型はあくまで参照型。実際のオブジェクトは CLI ヒープ上に存在するため、間接的に参照する場合はポインタではなくハンドルを使うことになる。つまり * → ^, & → % のルールで自然に振舞うということ。ここまでは問題ない。混乱の元は値型だ。

続きを読む

ハンドルとポインタと参照

  • Posted by: zakio
  • 2009年2月25日 21:56
  • C++/CLI

C++ に慣れ親しんだ私は、C++/CLI のハンドルを CLI heap 用のポインタという風に理解している。native heap は new で確保してポインタへ、CLI heap は gcnew で確保してハンドルへという単純な話だ。それだけならわざわざ取り上げる必要もないが、色々と調べているうちに混乱してきたので書きながら整理してみようと思う。

まずは単純な話から。


using namespace std;
using namespace System::Collections::Generic;

vector<int>* pVec = new vector<int>; // native heap を new で確保して pointer で受ける
List<int>^ hList = gcnew List<int>; // CLI heap を gcnew で確保して handle で受ける

// -> は同じ
pVec->push_back(1);
hList->Add(1);

// * も同じ
(*pVec).push_back(2);
(*hList).Add(2);

// delete も同じ
delete pVec;
delete hList;

vector に対応するものが List という点が気持ち悪いが、まぁこれはそういうもの。大事なのは new → gcnew, * → ^ という対応の部分だ。一旦領域を確保してしまえば、後は C++ と同じように扱える。

続きを読む

Index of all entries

Home > C++/CLI Archive

Search
Feeds

Return to page top