C++ での Object Pascal 無名メソッドの扱い方

提供: Appmethod Topics
移動先: 案内検索

Appmethod C++ での Object Pascal 機能の取り扱い:インデックス への移動

Object Pascal の最新機能の 1 つである無名メソッドを扱う際に発生するおそれのあるプログラミング上の問題をいくつか説明します。

Object Pascal の内部では、Invoke(...) メソッドを実装するインターフェイスを通じて無名メソッド型(メソッド参照とも呼ばれます)を実装しています。そのため、Object Pascal でメソッド参照パラメータを取るメソッドは、インターフェイスをパラメータに取るメソッドとして C++ に公開されます。以下はその例です。

 interface
 
 type
 
   TRefProc = reference to function (I, J: Integer): Integer;
 
   TTestClass = class
     public
       function TakeRefProc(RefProc: TRefProc; I, J: Integer): Integer;
   end;

上記に対して生成される .hpp ファイルは以下のとおりです。

 __interface TRefProc;
 typedef System::DelphiInterface <TRefProc> _di_TrefProc;
 
 __interface TRefProc  : public System::IInterface
 {
 public:
  virtual int __fastcall Invoke(int I, int J) = 0 ;
 };
 
 class PASCALIMPLEMENTATION TTestClass : public System::TObject
 {
 public:
  int __fastcall TakeRefProc(_di_TRefProc RefProc, int I, int J);
 };

関数やメンバ関数をメソッド参照パラメータとして指定しようとする C++ コードでは、Invoke() メソッドを公開するインターフェイスで後者をラップする必要があります。C++ テンプレートを用いると、そのようなインターフェイスをカプセル化することができます。以下の C++ コードでは、C++ の関数やメンバ関数をメソッド参照として Object Pascal に渡すのに使用できるテンプレートの例を示しています。

 enum _DummyType{};      // デフォルトとして使用されるパラメータ
 
 template <typename INTF,   // Invoke を持つインターフェイス
                 typename F,   // 関数の型
                 typename R,   // 戻り値の型
                 typename P1 = _DummyType,  // 第 1 パラメータ
                 typename P2 = _DummyType,  // 第 2 パラメータ
                 typename P3 = _DummyType,  // 第 3 パラメータ
                 typename P4 = _DummyType,  // 第 4 パラメータ
                 typename P5 = _DummyType> // 第 5 パラメータ
 class TMethodRef : public TInterfacedObject, public INTF
 
 {
 private:
   F callback;
 public:
   TMethodRef(F _callback) : callback(_callback) {}
 
   HRESULT STDMETHODCALLTYPE QueryInterface (const GUID& riid, void** ppvObject)
   { return TInterfacedObject::QueryInterface (riid, ppvObject); }
   ULONG STDMETHODCALLTYPE AddRef()
   { return TInterfacedObject::_AddRef(); }
   ULONG STDMETHODCALLTYPE Release()
   { return TInterfacedObject::_Release(); }
 
 R __fastcall Invoke(P1 p1)
   {
     return callback(p1);
   }
   R __fastcall Invoke(P1 p1, P2 p2)
   {
     return callback(p1, p2);
   }
   R __fastcall Invoke(P1 p1, P2 p2, P3 p3)
   {
     return callback(p1, p2, p3);
   }
   R __fastcall Invoke(P1 p1, P2 p2, P3 p3, P4 p4)
   {
     return callback(p1, p2, p3, p4);
   }
   R __fastcall Invoke(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
   {
     return callback(p1, p2, p3, p4, p5);
   }
 };

上記のテンプレートを用いて C++ ルーチンをメソッド参照として渡す方法を以下のコードに示します。

 // メソッド参照として渡す C++ 関数
 int multiplyCallback(int i, int j)
 {
   return i*j;
 }
 
 
 void UseRefProcFlat()
 {
   std::auto_ptr<TTestClass> cls(new TTestClass());
   _di_TRefProc proc = new 
     TMethodRef<TRefProc, int (*)(int, int), int, int, int>(multiplyCallback);
   int i = cls->TakeRefProc(proc, 10, 20);
   assert(i == 200);
 }

テンプレートを用いてメンバ関数をメソッド参照として渡すこともできます。以下のコードはその例です。

 class TMyClass {
 public:
   int add(int i, int j) {
     return i+j;
   }
 };
 typedef int (__closure *TClosure)(int, int);
 
 void UseRefProcMember()
 {
   TMyClass myClass;
 
   std::auto_ptr<TTestClass> cls(new TTestClass());
   _di_TRefProc proc = new 
     TMethodRef<TRefProc, TClosure, int, int, int>(&myClass.add);
 
   int i = cls->TakeRefProc(proc, 10, 20);
   assert(i == 30);
 }

関連項目