[译] 如何在 C++ Builder 使用 Delphi 中的泛型

……省略废话若干……

Delphi的泛型在C++中被暴露为模板。然而很重要的一点是它的真正实例化于 Delphi 那边,而不是C++。所以,你只能使用那些 Delphi 代码中已经显式实例化的模板类型。例如,我们声明一个 Delphi 中的简单泛型 TList<T>:

unit DelphiUnit;
 
interface
  uses System.Generics.Collections;
 
type
  MyTList<T> = class(TList<T>)
  public
    // Anchors constructor/destructor
    constructor Create;
    destructor Destroy; override;
 
    class procedure Cleanup(var L: MyTList<T>); static;
  end;
 
  // DoubleList: instantiates MyTList<double>
  DoubleList = class(MyTList<double>)
  end;
 
  // StringList: instantiates MyTList<string>
  StringList = class(MyTList<string>)
  end;
 
implementation
 
class procedure MyTList<T>.Cleanup(var L: MyTList<T>);
begin
  L.Free;
end;
 
constructor MyTList<T>.Create;
begin
  inherited;
end;
 
destructor MyTList<T>.Destroy;
begin
  inherited;
end;
 
end.

上面的接口在 C++ 中将暴露为:

// CodeGear C++Builder
// Copyright (c) 1995, 2012 by Embarcadero Technologies, Inc.
// All rights reserved
 
// (DO NOT EDIT: machine generated header) 'DelphiUnit.pas' rev: 24.00 (Windows)
 
#ifndef DelphiunitHPP
#define DelphiunitHPP
 
#pragma delphiheader begin
#pragma option push
#pragma option -w       // Display all warnings
#pragma option -w-inl   // Functions %s are not expanded inline
#pragma option -w-8111  // Accessing deprecated entity
#pragma option -Vx      // Zero-length empty class member
#pragma pack(push,8)
#include <System.hpp>	// Pascal unit
#include <SysInit.hpp>	// Pascal unit
#include <System.Generics.Collections.hpp>	// Pascal unit
#include <System.Generics.Defaults.hpp>	// Pascal unit
#include <System.Types.hpp>	// Pascal unit
 
//-- user supplied -----------------------------------------------------------
 
namespace Delphiunit
{
//-- type declarations -------------------------------------------------------
template<typename T> class DELPHICLASS MyTList__1;
// Template declaration generated by Delphi parameterized types is
// used only for accessing Delphi variables and fields.
// Don't instantiate with new type parameters in user code.
template<typename T> class PASCALIMPLEMENTATION MyTList__1 : public System::Generics::Collections::TList__1<T>
{
	typedef System::Generics::Collections::TList__1<T> inherited;
 
public:
	__fastcall MyTList__1(void);
	__fastcall virtual ~MyTList__1(void);
	static void __fastcall Cleanup(MyTList__1<T>* &L);
};
 
 
class DELPHICLASS DoubleList;
class PASCALIMPLEMENTATION DoubleList : public MyTList__1<double>
{
	typedef MyTList__1<double> inherited;
 
public:
	/* {DelphiUnit}MyTList<System_Double>.Create */ inline __fastcall DoubleList(void) : MyTList__1<double>() { }
	/* {DelphiUnit}MyTList<System_Double>.Destroy */ inline __fastcall virtual ~DoubleList(void) { }
 
};
 
 
class DELPHICLASS StringList;
class PASCALIMPLEMENTATION StringList : public MyTList__1<System::UnicodeString>
{
	typedef MyTList__1<System::UnicodeString> inherited;
 
public:
	/* {DelphiUnit}MyTList<System_string>.Create */ inline __fastcall StringList(void) : MyTList__1<System::UnicodeString>() { }
	/* {DelphiUnit}MyTList<System_string>.Destroy */ inline __fastcall virtual ~StringList(void) { }
 
};
 
 
//-- var, const, procedure ---------------------------------------------------
}	/* namespace Delphiunit */
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_DELPHIUNIT)
using namespace Delphiunit;
#endif
#pragma pack(pop)
#pragma option pop
 
#pragma delphiheader end.
//-- end unit ----------------------------------------------------------------
#endif	// DelphiunitHPP

C++ 代码链接上面的 Delphi 单元产生的 .obj 就可以使用 MyTList__1<double> 或 MyTList__1<System::String> 实例。

void UseDLists()
{
  // C++ code can use the Generics defined in Delphi directly
  // as long as the C++ code limits itself to types for which
  // the generic was instantiated on the Delphi side. For example,
  // since the Delphi Unit instantiates MyTList<String>
  // and MyTList<double> we can use these here.
  // However, if we try to use MyTList__1<char> we'll get
  // errors since the Delphi side did not instantiate
  // MyTList<AnsiChar>.
  MyTList__1<double>* dblList = new MyTList__1<double>();
  dblList->Add(1.0);
  dblList->Add(1.5);
  double d = dblList->Items[1];
#ifdef _WIN64
  delete dblList
#else
  MyTList__1<double>::Cleanup(dblList);
#endif
 
  MyTList__1<System::String> *strList = new MyTList__1<System::String>();
  strList->Add("First");
  strList->Add("Second");
  strList->Add("Third");
  assert(strList->Count == 3);
 
  System::String str = strList->Items[0];
  assert(str == "First");
  assert(strList->Items[1] == "Second");
  assert(strList->Items[2] == "Third");
 
  strList->Insert(0, "Inserted");
  assert(strList->Count == 4);
  assert(strList->Items[0] == "Inserted");
  assert(strList->Items[1] == "First");
 
  strList->Reverse();
  assert(strList->Items[0] == "Third");
  assert(strList->Items[1] == "Second");
  assert(strList->Items[2] == "First");
  assert(strList->Items[3] == "Inserted");
 
  assert(strList->Contains("Inserted"));
  assert(!strList->Contains("Not Inserted"));
 
  strList->Sort();
  strList->Remove("Inserted");
 
  assert(strList->Items[0] == "First");
  assert(strList->Items[1] == "Second");
  assert(strList->Items[2] == "Third");
 
#ifdef _WIN64
  delete strList;
#else
  MyTList__1<System::String>::Cleanup(strList);
#endif
}

如果你的 C++ 代码尝试使用一个没有实例化的 Delphi 泛型,你将在链接时得到错误提示。例如下面的代码尝试使用Delphi 代码中没有明确实例化为  MyTList<AnsiChar> 的  MyTList__1<char> :

void UseListOfChar()
{
  MyTList__1<char>* charList = new MyTList__1<char>();
  charList->Add('a');
  // ...
}

当上面的代码编译后,在链接时将报告下面的错误:

[ilink32 Error] Error: Unresolved external 'Delphiunit::MyTList__1<char>::' referenced from CPPUNIT.OBJ
[ilink32 Error] Error: Unresolved external '__fastcall Delphiunit::MyTList__1<char>::MyTList__1<char>()' referenced from CPPUNIT.OBJ
[ilink32 Error] Error: Unresolved external '__fastcall System::Generics::Collections::TList__1<char>::Add(const const char)' referenced from CPPUNIT.OBJ
[ilink32 Error] Error: Unable to perform link

要解决这个问题,在 Delphi 中声明类型 TMyList<AnsiChar>。

 

分享到: