特集
Visual C++ 2005

いままたC++が熱い!「C++/CLI」として大進化したVisual C++ 2005

株式会社ピーデー 川俣 晶
2005/08/31
Page1 Page2 Page3

■マネージ・コードとネイティブ・コードの混在

 面白いので、もう1つ見ていただこう。(先ほどは単にアンマネージ・コードの呼び出しをマネージ・コードのソース・コード上に記述しただけだったが)実は、マネージ・コードとネイティブ・コードを1つのソース・コード上に混在させることもできる。このような混在は、マネージ・コードとネイティブ・コードの仕様の非互換性(例えばマネージ・コードではインライン・アセンブラを使用できない)を回避するなどの目的で使用できる。

 しかし、やり方は簡単。「#pragma managed」を書けばそれ以降の行はマネージ・コードになり、「#pragma unmanaged」と書けばそれ以降の行はネイティブ・コードになってくれるのである。

#include "stdafx.h"
#include <stdio.h>

#using <mscorlib.dll>

using namespace System;

#pragma unmanaged

void unmanagedFunc()
{
  printf("unmanaged function\n");
}

#pragma managed

int _tmain()
{
  unmanagedFunc();
  Console::WriteLine(S"managed function");
  return 0;
}
マネージ・コードとネイティブ・コードを混在させたC++マネージ拡張のコード
Visual Studio .NET 2003にて、[Visual C++]の[コンソールアプリケーション(.NET)]プロジェクトを作成して入力する。「#pragma managed」を書けばそれ以降の行はマネージ・コードになり、「#pragma unmanaged」と書けばそれ以降の行はネイティブ・コードとなる。

 これを実行すると以下のようになる。

 ここで、unmanagedFunc関数は#pragma unmanagedという行の後に記述されているのでunmanagedなネイティブ・コードの関数である。しかし、_tmain関数は#pragma managedという行の後に記述されているので、マネージ・コードである。

 このように、C++マネージ拡張は過去の資産を丸ごと飲み込むだけの互換性と機能を持っている。つまり、Webやイントラネットの時代が到来する前にあったWindows黄金時代のC++資産を受け継ぎ、新しい時代に適合させるのに最適なプログラミング言語ということができる。

[コラム]
MFCアプリケーションで.NET Frameworkクラス・ライブラリを使う
 筆者の個人サイトで公開したサンプル・コードだが、興味深いのでここで紹介しよう。なんと、MFCアプリケーションで.NET Frameworkクラス・ライブラリを使うというコードである。これは、CViewクラスを継承したビューのOnDrawハンドラで、.NET Frameworkクラス・ライブラリの機能を用いて描画を行う。

 作成手順は、以下のとおりである。まず、Visual Studio .NET 2003を使用し、通常のWin32 MFCアプリケーションのプロジェクトを作成する。そして、プロジェクトのプロパティから「マネージ拡張」を「はい」に切り替える。その後、ビューを実現する.cppファイルに対して、以下のように書き加える。

……前略……

#using <mscorlib.dll>
#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;

……中略……

void Cmfcdotnet001View::OnDraw(CDC* pDC)
{
  Cmfcdotnet001Doc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;

#pragma push_macro ("new")
#undef new
  Graphics * g = Graphics::FromHdc(pDC->m_hDC);
  try
  {
    Font * font = __gc new Font(S"MS ゴシック",12);
    Brush * brush = __gc new SolidBrush( Color::Black );
    PointF position( 10.0, 10.0 );
    g->DrawString( S"Hello Mixed!", font, brush, position );
  }
  __finally
  {
    g->Dispose();
  }
#pragma pop_macro ("new")
MFCアプリケーションにおける.NET Frameworkクラス・ライブラリの利用例

 これを実行すると以下のようになる。

 ちなみに、これによって作成される実行ファイルは、マネージ・コードとして記述されたMFCアプリケーションである。つまり、.NET FrameworkとMFCは排他的な関係ではなく、.NET Framework時代にもMFCを使い続けるという選択があり得るのである。

C++マネージ拡張の物足りない部分

 C++マネージ拡張の技術的な面白さは筆舌に尽くしがたい。しかし、いくら面白くても、C++マネージ拡張が完ぺきであるとは思えない。実際に使うと、いろいろと不満が出てくる。

 例えばマネージなオブジェクトを生成するために、いちいち「__gc new」と書くのは面倒だし、これは読みにくい。しかも、gcの手前に付いたアンダースコア2文字は、いかにも非標準キーワードという感じで肩身が狭い。

 「for each構文」の不在もやはり実際にコーディングしていて痛い。対象や好みによっても変わると思うが、筆者がC#でコーディングする場合、恐らく繰り返しの半分以上はforeachステートメントを利用する。Javaでも、あえて言語仕様を拡張してまで導入した構文であるから、有効性は筆者個人の思い込みではないだろう。それを欠くC++マネージ拡張は、やはり痛い。

 もう1つ、C#に頭のてっぺんまで漬かったプログラマの感覚でいうと、usingステートメントの不在は、大きな問題といえる。usingステートメントとは、つまりDisposeパターンを使った明示的な解放を実現するリソース管理機能である。これは、ファイル・ハンドルのような解放のタイミングが大きな意味を持つリソースを扱う場合には、ソース・コードを簡潔に記述するために大きな力を発揮してくれる。特に使ったものはきちんと片付けないと気が済まない性格ならば、usingステートメントの有無は精神衛生に大いに影響するといえる。

 ほかにも問題はあると思うが、取りあえず筆者が特に気にしている問題を3つ紹介してみた。

 以下に、C++マネージ拡張への不満を表現した一例として、ファイルをコピーするサンプル・プログラムを紹介する。これは、コマンドライン引数で指定されたファイルのコピーを行うが、サンプルなので親切なエラー処理などは含めていない。

#include "stdafx.h"

#using <mscorlib.dll>

using namespace System;
using namespace System::IO;

int _tmain(int argc, _TCHAR* argv[])
{
  FileStream * inputStream =
    __gc new FileStream(argv[1],FileMode::Open);
  try
  {
    FileStream * outputStream =
      __gc new FileStream(argv[2],FileMode::Create);
    try
    {
      for(;;)
      {
        Byte ar[] = __gc new Byte[1024];
        int size = inputStream->Read(ar,0,ar->Length);
        if( size == 0 ) break;
        outputStream->Write(ar,0,size);
      }
    }
    __finally
    {
      outputStream->Close();
    }
  }
  __finally
  {
    inputStream->Close();
  }
  return 0;
}
ファイルのコピーを行うサンプル・プログラム(C++マネージ拡張版)
Visual Studio .NET 2003にて、[Visual C++]の[コンソールアプリケーション(.NET)]プロジェクトを作成して入力する。

 ちなみに、同じものをC#で書くと以下のようになる。__finallyキーワードも、Closeメソッド呼び出しも存在せず、かなりすっきりしていることが分かると思う。もちろん、usingステートメントのブロックを抜けるときに、ファイルはきちんと閉じられる。

using System;
using System.IO;

class OldStyleByCs
{
  [STAThread]
  static int Main(string[] args)
  {
    using( FileStream inputStream
        = new FileStream(args[0],FileMode.Open) )
    {
      using( FileStream outputStream
          = new FileStream(args[1],FileMode.Create) )
      {
        for(;;)
        {
          byte [] ar = new byte[1024];
          int size = inputStream.Read(ar,0,ar.Length);
          if( size == 0 ) break;
          outputStream.Write(ar,0,size);
        }
      }
    }
    return 0;
  }
}
ファイルのコピーを行うサンプル・プログラム(C#版)
Visual Studio .NET 2003にて、[Visual C#]の[コンソールアプリケーション]プロジェクトを作成して入力する。

 このような状況を見ると、C++マネージ拡張は日常の開発で喜んで使う言語ではない……という印象を感じてしまう。確かに技術的に面白いし、これを使わねば開発できないプログラムの開発に使う価値も感じる。だが、これを主力開発言語と呼ぶには、ためらいを感じてしまう。

 しかし、われわれにはまだ未来への希望がある。それが、Visual C++ 2005(=Visual Studio 2005のC++機能)とC++/CLIである。


 INDEX
  [特集] Visual C++ 2005
  いままたC++が熱い!「C++/CLI」として大進化したVisual C++ 2005
    1. 本当はすごいC++マネージ拡張
  2. C++マネージ拡張の物足りない部分
    3. Visual C++ 2005とC++/CLIという解決
 


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 記事ランキング

本日 月間