[译] 如何在 C++ Builder 中使用 Delphi 的匿名方法

回到 在 C++ Builder 中使用 Delphi 特性

本主题描述了一些你可能在面对匿名函数时遇到的编程问题-一个 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;

下面是根据上面生成的 .hpp 文件:

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++ 模板可以用来封装一个接口并暴露 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;
}

 

[俺的评价:依然很不舒服]

  • 使用一上 Lamda 表达式

当一个 API 要求一个匿名函数时,你也可以使用一个 Lambda 表达式。Delphi 接口类被更新以自动转换为 lambda 表达式。这个特性仅适用于基于 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;

[好吧,舒服多了,不过遗憾的是 BCC32 明显不支持这种表达方式]

滚动至顶部