特集

Vista時代のVisual C++の流儀(後編)

STL/CLRによるDocument/Viewアーキテクチャ

επιστημη(えぴすてーめー)
2007/05/11
Page1 Page2 Page3 Page4

STL/CLRの特徴

 本特集の中編ではMFCで実装されたGUIアプリケーションのDocument/Viewアーキテクチャを踏襲し、DocumentをC++/CLIで、ViewをC#で実現する手法を解説しました。その中で“できるだけネイティブなC++コードを残す”ための例として、ネイティブ・コードのSTLコンテナを、マネージ・クラス内に内包させるコードを紹介しています。

 このスタイルの欠点は、マネージ・クラス内のネイティブ・メンバはポインタでなくてはならず、そのためにネイティブ・メンバの確保と解放(newとdelete)はプログラマーが責任を負わなければならないこと、さらにネイティブなコンテナはそれが管理する要素がネイティブ・オブジェクトに限られる(マネージ・オブジェクトを要素とすることができない)ためにネイティブ⇔マネージの相互変換を頻繁に行わざるを得ず、パフォーマンスを損なう恐れがあることです。

public ref class StringStackDocument : StringStackInterface {
private:
  std::vector<std::wstring>* container_;
  event System::EventHandler^ handler_;
public:
  // コンストラクタでcontainer_オブジェクトを生成
  StringStackDocument() {
          container_ = new std::vector<std::wstring>(); }
  // デストラクタ/ファイナライザで解放
~StringStackDocument() { this->!StringStackDocument(); }
!StringStackDocument() { delete container_; }

  virtual void Push(System::String^ element) {
    // 必要に応じてマネージ(String)→ネイティブ(wstring)変換
    container_->push_back(FromCLI(element));
    handler_(this, nullptr);
  }
  ……省略……
};
標準C++ライブラリのコンテナ(std::vectorクラス)による文字列の可変長配列
マネージ・クラス内でネイティブ・メンバを使う際の欠点は、ネイティブ・オブジェクトの確保と解放が必要なこと。さらに、ネイティブ⇔マネージの相互変換を頻繁に行わざるを得ないこと。

 STL/CLRは.NET Frameworkが提供するコンテナ群と標準C++ライブラリのコンテナ群の“合いの子”のようなライブラリです。つまりマネージ・コンテナでありながらそのインターフェイスはSTLを踏襲しています。“マネージ版STL”といってもいいでしょう。

#include <cliext/vector>

namespace dotNet {
  public ref class StringStackDocument : StringStackInterface {
    ……省略……
  private:
    // ポインタを介在しない(ハンドル「^」をメンバとしてもよい
    cliext::vector<System::String^> container_;
    event System::EventHandler^ handler_;
  };
}
STL/CLRのコンテナ(cliext::vectorクラス)による文字列の可変長配列
マネージ・メンバであれば、確保と解放の必要がなく、ネイティブ⇔マネージの相互変換もしなくてよい。

 STL/CLRコンテナはマネージ・クラスでありポインタを介さないので、デストラクタ/ファイナライザでdeleteする必要はありません。加えてコンテナの要素としてマネージ・オブジェクトを格納できるため、ネイティブ⇔マネージ変換を要する個所が少なくなるため、次のコード例のように実装コードの見通しがよくなります。

namespace dotNet {
  ……省略……
  void StringStackDocument::Push(System::String^ element) {
    // container_がハンドルの場合、container_->push_back(element)
    container_.push_back(element);
    handler_(this, nullptr);
  }

  System::String^ StringStackDocument::Pop() {
    System::String^ result = container_.back();
    container_.pop_back();
    handler_(this, nullptr);
    return result;
  }
}
STL/CLRのコンテナ(cliext::vectorクラス)によりネイティブ⇔マネージ変換が不要になったコード例

 STL/CLRが提供するコンテナと、それぞれに(機能的に)等価な.NET Frameworkコレクション(System::Collections::Generic名前空間)およびMFCテンプレート・コレクションとの対応表を以下に示します。

コレクションの種類 STL/CLRコンテナ
(cliext名前空間)
.NET Frameworkコレクション
(System::Collections::Generic 名前空間)
MFCテンプレート・コレクション
可変長配列 vector<T><T> List<T> CArray<T>
双方向リスト list<T> LinkedList<T> CList<T>
両端キュー deque<T>
スタック stack<T> Stack<T>
キュー queue<T> Queue<T>
優先順位キュー priority_queue<T>
ソートされた集合
(要素重複可)
set<T>
multiset<T>
ソートされた辞書
(要素重複可)
map<K,V> SortedDictionary<K,V>
multimap<K,V>
ハッシュ集合
(要素重複可)
hash_set<T>
hash_multiset<T>
ハッシュ辞書
(要素重複可)
hash_map<K,V> Dictionary<K,V> CMap<K,V>
hash_multimap<K,V>
STL/CLRが提供するコンテナと.NETやMFCのコンテナとの対応表
灰色部分は「対応するコンテナが存在しない」ことを示す。

 このように、コンテナのバリエーションではSTL/CLRが最も多く、C++&MFCやManaged C++&.NET Frameworkで実装されたアプリケーションをC++/CLIに移植する際に、対応するコンテナに悩むことはないでしょう。

 STL/CLRが提供するコンテナはマネージ・クラスでありながらジェネリック(Generic)ではなくC++のテンプレート(template)で実装されています。

 テンプレートの実現メカニズムは、(.NETの)ジェネリックとは大きく異なり、コンパイル時に<>内の型が確定した時点でコード展開が行われます。template(テンプレート)はC#やVBなどの.NET言語では解釈できず、コンパイル・エラーとなります。従ってSTL/CLRはC++/CLI専用のライブラリということになります。

 端的にいえば、C#では例えば、

new cliext.vector<string>()

によってSTL/CLRコンテナを生成することはできませんし、C++/CLIで生成されたアセンブリ内のメソッドからの戻り値としてSTL/CLRコンテナを返すこともできませんから、

cliext.vector<string> result = STL_CLR_lib.SomeClass.get_container();

などとやってもコンパイル・エラーとなります。

 これではSTL/CLRがC++/CLI専用ということになり、いささか扱いにくいのですが、幸いなことにSTL/CLRコンテナはすべてICollection<T>インターフェイスを実装しています。なのでC++/CLI側のアセンブリ内メソッドがICollection<T>オブジェクトを戻り値に返せば、C#やVBでこれを受け取れます。また、ICollection<T>は同時にICollection/IEnumerable<T>/IEnumerableインターフェイスを継承したインターフェイスでもあるので、foreachキーワード(C#)やFor Each...Nextステートメント(VB)によってコンテナ内の要素を列挙することができます。

 cliext::vector<T>クラスを例に取ると、vector<T>のクラス階層は下図のようになります。

vector<T>クラスのクラス階層

 現行製品のVS 2005で試してみましょう。


 INDEX
  [特集]
  Vista時代のVisual C++の流儀(前編)
  Vista到来。既存C/C++資産の.NET化を始めよう!
    1.Vista時代にC/C++はもはやお払い箱なのか?
    2.C/C++資産をどこまで生かせる?
    3.ネイティブ・オブジェクトをマネージ・コードでくるむ
    4.文字コード変換
 
  Vista時代のVisual C++の流儀(中編)
  MFCから.NETへの実践的移行計画
    1.C++/CLIによるWindowsフォーム・アプリケーション
    2.言語をまたいだDocument/Viewアーキテクチャ
    3..NET移行前のMFCサンプル・アプリケーション
    4.MFCのDocument/Viewアーキテクチャの.NET化
    5.MFCで書かれたDocumentを.NET化する2つの方法
 
  Vista時代のVisual C++の流儀(後編)
  STL/CLRによるDocument/Viewアーキテクチャ
    1.STL/CLRとは
  2.STL/CLRの特徴
    3.Visual Studio 2005で試す
    4.おまけ:NUnitの活用


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間