Comment gérer les génériques Object Pascal dans C++

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Gestion des fonctionnalités Object Pascal dans Appmethod C++ - Index

Cette rubrique décrit quelques problèmes de programmation que vous pourriez rencontrer avec les génériques, une fonctionnalité supportée par Object Pascal.

Les génériques Object Pascal sont exposés dans C++ comme des templates. Toutefois, il est important de réaliser que les instanciations surviennent du côté Object Pascal, pas dans C++. Par conséquent, vous pouvez seulement utiliser ces templates pour les types qui étaient instanciés explicitement dans le code Object Pascal. Par exemple, déclarons un simple générique, TList<T>, dans Object Pascal :

 
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.

L'interface ci-dessus est exposée dans C++ comme suit :

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

La liaison du code C++ avec le fichier .obj créé à partir de l'unité Object Pascal ci-dessus peut utiliser des instances de MyTList__1<double> ou 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
}

Si le code C++ tente d'utiliser un générique Object Pascal pour les types qui n'étaient pas instanciés dans Object Pascal, vous obtiendrez des erreurs lors de la liaison. Par exemple, le code suivant tente d'utiliser MyTList__1<char> quand le code Object Pascal n'instanciait pas explicitement MyTList<AnsiChar> :

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

Tandis que le code ci-dessus est correctement compilé, les erreurs suivantes sont générées lors de la liaison :


 
[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

Pour éliminer l'erreur, assurez-vous que le code Object Pascal utilise le type MyTList<AnsiChar>.

Voir aussi