RAD Studio
ContentsIndex
PreviousUpNext
How to Handle Delphi Generics in C++

This topic describes some programming issues that you might encounter when dealing with generics, one of Delphi's newest features. 

Delphi generics are exposed to C++ as templates. However, it's important to realize that the instantiations occur on the Delphi side, not in C++. Therefore, you can only use these template for types that were explicitly instantiated in Delphi code. For example, let's declare a simply generic, TList<T>, in Delphi:

 interface

type

    MyTList<T> = class(TList<T>) // TList is a class in the Generics.Collections namespace
        FItems: array of T;
    protected
        function GetLength: Integer;
    public
        function Get(Index: Integer): T;
    published
        property Len: Integer read GetLength;
    end;
  
    { ScoreList derived from a TList<double>}
    ScoreList = class(MyTList<double>)
    end;
    { StringList derived from a TList<string>}
    StringList = class(MyTList<string>)
    end;

implementation

{$R *.dfm}

function MyTList<T>.GetLength: Integer;
begin
    Result := Count;
end;
  
  function MyTList<T>.Get(Index: Integer): T;
begin
    Result := Items[Index];
end;

The interface above is exposed to C++ as the following:

// 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 Generics_collections::TList__1<T>
{
typedef Generics_collections::TList__1<T> inherited;
      
private:
typedef DynamicArray<T> _MyTList__1__1;
        
public:
    _MyTList__1__1 FItems;
        
 protected:
    int __fastcall GetLength(void);
        
public:
    T __fastcall Get(int Index);
        
__published:
    __property int Len = {read=GetLength, nodefault};
public:
/* TList<T>.Create */ inline __fastcall MyTList__1(void)/* overload */ : Generics_collections::TList__1<T>() { }
      /* TList<T>.Destroy */ inline __fastcall virtual ~MyTList__1(void) { }
            
};          
            
class DELPHICLASS ScoreList;
class PASCALIMPLEMENTATION ScoreList : public MyTList__1<double>
{
typedef MyTList__1<double> inherited;
                
public:
/* TList<Double>.Create */ inline __fastcall ScoreList(void)/* overload */ : MyTList__1<double>() { }
      /* TList<Double>.Destroy */ inline __fastcall virtual ~ScoreList(void) { }
                    
};
                                        
class DELPHICLASS StringList;
class PASCALIMPLEMENTATION StringList : public MyTList__1<System::UnicodeString>
{
typedef MyTList__1<System::UnicodeString> inherited;
                        
public:
/* TList<string>.Create */ inline __fastcall StringList(void)/* overload */ : MyTList__1<System::UnicodeString>() { }
/* TList<string>.Destroy */ inline __fastcall virtual ~StringList(void) { }
                            
};
                            

C++ code linking with the .obj created from the above Delphi unit can use instances of TList__1<double> or ScoreList.

void UseScoreList()
{
  ScoreList* list = new ScoreList();

  list->Add(1.0);
  list->Add(2.0);

  int len = list->Len;
  assert(len == 2);

  delete list;
}

void UseTList__1()


{
    // 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 size. For example,
    // since test.pas uses TList<String> and TList<double> we can use
    // these here. However, if we try to use TList__1>char> we'll get
    // an error since the Delphi side did not instantiate
    // TList<AnsiChar>.
    TList__1<double>* dblList = new MyTList__1<double>();
    dblList—>Add(1.0);
    dblList—>Add(1.5);
    double d = dblList—>Get(1);
    delete dblList;

    MyTList__1<UnicodeString> *stringList = new MyTList__1<UnicodeString>();
    stringList->Add("hiya");
    stringList->Add("there");
    stringList->Add("buckeroo");
    UnicodeString dstring = stringList->Get(0);
    delete stringList;
 } 

If C++ code attempts to use a Delphi generic for types that were not instantiated in Delphi, you'll get errors at link time. For example, the following code attempts to use TList__1<char> when the Delphi code did not explicitly instantiate TList<AnsiChar>:

void UseListOfChar()
{
  TList__1<char>* charList = new TList__1<char>();
charList->Add('a');
  char ch = charList->Get(1);
  delete charList;
}

While the code above compiles, the following errors are generated at link time:

[ILINK32 Error] Error: Unresolved external 'Test::MyTList__1<char>::>::' referenced from USETEST.OBJ
[ILINK32 Error] Error: Unresolved external '__fastcall Test::MyTList__1<char>>::Add(char)' referenced from USETEST.OBJ
[ILINK32 Error] Error: Unresolved external '__fastcall Test::MyTList__1<char>::Get(int)' referenced from USETEST.OBJ

To eliminate the error, you have to make sure that the Delphi code uses the type MyTList<AnsiChar>.

Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!