Comment gérer les génériques 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 génériques, une fonctionnalité prise en charge par Delphi.

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

 
unit MyUnit;

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 :

#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:
    /* {MyUnit}MyTList<System_Double>.Create */ inline __fastcall DoubleList(void) : MyTList__1<double>() { }
    /* {MyUnit}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:
    /* {MyUnit}MyTList<System_string>.Create */ inline __fastcall StringList(void) : MyTList__1<System::UnicodeString>() { }
    /* {MyUnit}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é Delphi ci-dessus peut utiliser des instances de MyTList__1<double> ou MyTList__1<System::String>.

Le code C++ peut utiliser directement les génériques définis dans Delphi si le code C++ se limite à des types dont les génériques ont été instanciés côté Delphi. Par exemple, comme l'unité Delphi instancie MyTList<String> et MyTList<double>, ils peuvent être utilisés à cet endroit. Toutefois, si nous tentons d'utiliser MyTList__1<char>, nous obtiendrons des erreurs car le côté Delphi n'a pas instancié MyTList<AnsiChar>.

 
void UseDLists()
{
  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 Delphi pour les types qui n'étaient pas instanciés dans Delphi, vous obtiendrez des erreurs lors de la liaison. Par exemple, le code suivant tente d'utiliser MyTList__1<char> quand le code Delphi 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 :

[Erreur ILINK32] Erreur : 'Delphiunit::MyTList__1<char>::' externe non résolu, référencé depuis CPPUNIT.OBJ
[Erreur ILINK32] Erreur : '__fastcall Delphiunit::MyTList__1<char>::MyTList__1<char>()' externe non résolu, référencé depuis CPPUNIT.OBJ
[Erreur ILINK32] Erreur : '__fastcall System::Generics::Collections::TList__1<char>::Add(const const char)' externe non résolu, référencé depuis CPPUNIT.OBJ
[Erreur ILINK32] Erreur : Impossible d'effectuer la liaison

Pour éliminer l'erreur, vous devez vous assurer que le code Delphi utilise le type MyTList<AnsiChar>.

Voir aussi