How to Handle Delphi Anonymous Methods in C++
Go Up to Handling Delphi Features in C++Builder Index
This topic describes some programming issues that you might encounter when dealing with anonymous methods, one of Delphi's newer features.
Under the cover, Delphi implements anonymous methods types (also known as method references) via an interface that implements an Invoke(...)
method.
So a method that takes a method reference parameter in Delphi is exposed to C++ as a method that takes an interface. Here's an example:
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;
The following is the .hpp file generated for the above:
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 */;
As shown above, TFilterPredicate is exposed as an interface on the C++ side. C++ code that seeks to specify a function or member function as a method reference parameter has to wrap the latter behind an interface that exposes the Invoke()
method.
The following section describes two ways to implement that interface.
Using a Functor (Function Object)
A C++ template can be used to encapsulate an interface that exposes an Invoke() method.
The following C++ code shows an example of a template that can be used to pass C++ methods or member functions as method references to 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);
}
};
The following code shows how to use the template shown above to call 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;
}
Using a Lambda Expression
You can also use a lambda wherever an API expects an anonymous method. The DelphiInterface class has been updated to auto-convert to a lambda. This feature can only be used with the Clang-enhanced C++ Compilers.
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;
}