RAD Studio |  |

How to Handle Delphi Generics in C++
See Also
Collapse All

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:

Copy Code

     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:

Copy Code

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

Copy Code

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

Copy Code

    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:

Copy Code

    [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>.

See Also

Handling Anonymous Methods

Handling AnsiString Code Page Specification

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