Comment gérer les méthodes anonymes Delphi dans C++

De RAD Studio
Aller à : navigation, rechercher

Remonter à Gestion des fonctionnalités Delphi dans C++Builder - Index


Cette rubrique décrit quelques problèmes de programmation que vous pourriez rencontrer avec les méthodes anonymes, une fonctionnalité récente de Delphi.

Delphi implémente les types de méthodes anonymes (aussi connus comme des références de méthode) via une interface qui implémente une méthode Invoke(...).

Ainsi une méthode qui prend un paramètre référence de méthode dans Delphi est exposée dans C++ comme une méthode qui prend une interface. Par exemple :

 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;

Le code suivant est le fichier .hpp généré pour l'exemple ci-dessus :

  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 */;

Comme illustré ci-dessus, TFilterPredicate est exposé sous la forme d'une interface côté C++. Le code C++ qui effectue une opération seek pour spécifier une fonction ou une fonction membre comme paramètre référence de méthode doit envelopper la dernière derrière une interface qui expose la méthode Invoke().

La section suivante décrit deux manières d'implémenter cette interface.

Utilisation d'un foncteur (objet fonction)

Un template C++ peut être utilisé pour l'encapsulation d'une interface qui expose une méthode Invoke().

Le code C++ suivant montre un exemple d'un template qui peut être utilisé pour transmettre des fonctions membre ou des méthodes C++ en tant que références de méthode à 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);
  }
};

Le code suivant montre comment utiliser le template présenté ci-dessus pour appeler 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;
}

Utilisation d'une expression lambda

Vous pouvez également utiliser une expression lambda chaque fois qu'une API attend une méthode anonyme. La classe DelphiInterface a été mise à jour pour s'auto-convertir en expression lambda. Cette fonctionnalité peut seulement être utilisée avec les compilateurs C++ améliorés par Clang.

The following example illustrates:

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

Voir aussi