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

提供: RAD Studio
移動先: 案内検索

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


このトピックでは、Delphi の無名メソッド C++ コード内で処理する方法について説明します。

Delphi 無名メソッド型の実装

Delphi の内部では、Invoke(...) メソッドを実装するインターフェイスを通じて無名メソッド型(メソッド参照とも呼ばれます)を実装しています。

そのため、Delphi で、メソッド参照パラメータを取るメソッドは、インターフェイスをパラメータに取るメソッドとして 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;

以下は上記のために RAD Studio が提供する C++ ヘッダー ファイルです:

typedef System::DelphiInterface<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() メソッドを公開するインターフェイスで後者をラップする必要があります。

C++ における無名メソッド型の処理

ラムダ式を使用する

Clang 拡張 C++ コンパイラを使用する場合、Delphi API が Delphi 無名メソッドを想定するところでは、ラムダ式を使用することができます。DelphiInterface クラスは、ラムダ式に自動的に変換されます。

例:

#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;
}

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

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

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

#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;
}

関連項目