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

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

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


This topic describes some programming issues that you might encounter when dealing with anonymous methods, one of Object Pascal's newer features.

Under the cover, Object Pascal implements anonymous methods types (also known as method references) via an interface that implements an Invoke(...) method.

そのため、Object Pascal で、メソッド参照パラメータを取るメソッドは、インターフェイスをパラメータに取るメソッドとして C++ に公開されます。以下はその例です。

 interface

    type
      TFilterPredicate = reference to function(const Path: string;
          const SearchRec: TSearchRec): Boolean;
    // ...
    class function GetFiles(const Path: string;
        const Predicate: TFilterPredicate): TStringDynArray;
        overload; inline; static;

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

  typedef System::{{Delphi}}Interface<TFilterPredicate> _di_TFilterPredicate;
  __interface TFilterPredicate  : public System::IInterface 
  {
    virtual bool __fastcall Invoke(const System::UnicodeString Path, const System::Sysutils::TSearchRec &SearchRec) = 0 ;
  };
  // .. 
  static System::TStringDynArray __fastcall GetFiles(const System::UnicodeString Path, const _di_TFilterPredicate Predicate)/* overload */;

上記のとおり、TFilterPredicate は、C++ 側ではインターフェイスとして公開されます。関数やメンバ関数をメソッド参照パラメータとして指定しようとする C++ コードでは、Invoke() メソッドを公開するインターフェイスで後者をラップする必要があります。

そのインターフェイスの実装方法を 2 とおり次のセクションで説明します。

ファンクタ(Functor)(関数オブジェクト)を使用する

C++ テンプレートは、Invoke() メソッドを公開するインターフェイスをカプセル化するために使用することができます。

次の C++ コードは、C++ の関数やメンバ関数をメソッド参照として Object Pascal に渡すのに使用できるテンプレートの例を示しています。

#include <System.hpp>
 
enum _DummyType{};      // Parameter used as default
 
template <typename INTF, // Interface with Invoke
          typename F,    // Functor type
          typename R,    // Return type
          typename P1 = _DummyType,  // Param #1
          typename P2 = _DummyType,  // Param #2
          typename P3 = _DummyType,  // Param #3
          typename P4 = _DummyType,  // Param #4
          typename P5 = _DummyType>  // Param #5
class TMethodRef : public TCppInterfacedObject<INTF>
{
private:
  F callback;
public:
  TMethodRef(F _callback) : callback(_callback) {}
  INTFOBJECT_IMPL_IUNKNOWN(TInterfacedObject);
 
  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);
  }
};

次のコードは、上記のテンプレートを使用して GetFiles を呼び出す方法を示しています。

#include <System.IOUtils.hpp>
#include <iostream>
 
struct Filter {
  Filter(String _ext) : ext(_ext)
  {}
  bool operator()(const System::UnicodeString, const System::Sysutils::TSearchRec &SearchRec) {
    return ExtractFileExt(SearchRec.Name) == ext;
  }
  String ext;
};
 
int main()
{
  typedef TMethodRef<TDirectory::TFilterPredicate,
                     Filter,
                     bool,
                     const System::UnicodeString,
                     const System::Sysutils::TSearchRec&> MyMethRef;
  String ext(".cpp");                   
  TStringDynArray files = TDirectory::GetFiles(TDirectory::GetCurrentDirectory(),
                          TDirectory::_di_TFilterPredicate(new MyMethRef(Filter(ext))));
  std::cout << "Found " << files.Length 
            << " files with ext: '" << AnsiString(ext).c_str() << "'\n";
  for (int i=0; i<files.Length; ++i)
    std::cout << AnsiString(files[i]).c_str() << std::endl;
}

ラムダ(Lambda)式の利用

You can also use a lambda wherever an API expects an anonymous method. The Object PascalInterface class has been updated to auto-convert to a lambda. This feature can only be used with the Clang 拡張 C++ コンパイラ.

次に例を示します。

#include <System.hpp>
#include <System.IOUtils.hpp>
#include <iostream>
 
int main()
{
  String ext(".cpp");                   
  TStringDynArray files = TDirectory::GetFiles(TDirectory::GetCurrentDirectory(),
                          [ext](const String Path, const System::Sysutils::TSearchRec &SearchRec) -> bool
                          {
                            return ExtractFileExt(SearchRec.Name) == ext;
                          });
  std::cout << "Found " << files.Length 
            << " files with ext: '" << AnsiString(ext).c_str() << "'\n";
  for (int i=0; i<files.Length; ++i)
    std::cout << AnsiString(files[i]).c_str() << std::endl;
}

関連項目