C++ Iterator Support for Delphi Enumerable Types and Containers

From RAD Studio
Jump to: navigation, search

Go Up to Language Support for the RAD Studio Libraries (C++)


This page describes the current C++ iterator support for Delphi enumerable types and containers. The following table contains a summary of the supported types and containers:

Containers Iterator Type Iterator Traits Bcc Clang Type Range-for STL <algorithm> Support begin(), end()
System::DynamicArray<T> System::DynamicArray<T>::iterator random_access_iterator
Allowed.png
Allowed.png
By reference (read-write)
Allowed.png
(Clang Only)
Full Member,Non-member
TCollection (ie TList) System::TRandomIterator<T,E> random_access_iterator
Allowed.png
By value (read-only)
Allowed.png
(Clang Only)
Only non-modifying Non-member
TEnumerable System::TEnumerableIterator<T> input_iterator
Allowed.png
By value (read-only)
Allowed.png
(Clang Only)
Non-modifying, Non-random Non-member
TCollection (no TDictionary) System::TBackInsertIterator<T> back_insert_iterator
Allowed.png
Output iterator Write-only (see below) n/a
TCollection (no TDictionary) System::TInsertIterator<T> front_insert_iterator,insert_iterator
Allowed.png
Output iterator Write-only (see below) n/a

Guidelines

Most iterators are system-defined, and as templates are compiler-selected through ADL and SFINAE. This means they work for the majority of types, but unusual interfaces or behaviour in your own types may require you to write an iterator. For example, TDictionary, despite being enumerable, does not support random access at an index and therefore cannot use random_access_iterator. It has its own iterator defined. The following are guidelines that can be used as a rule of thumb for which iterators are available for a type.

  • If the type only has a GetEnumerator() method, you can use Range-for and a limited set of read-only STL Algorithms.
  • If the type has a subscript operator[], you can use Range-for and all the read-only STL Algorithms.
Note: TDictionary is subscriptable but does not allow random access. It's a TEnumerable.
  • If the the type is a kind of System::DynamicArray<T>, you can use Range-for and the full set of STL ALgorithms. Keep in mind that many Delphi RTL types are "aliases" for System::DynamicArray<T>, such as Arrayofstring, TIntegerDynArray, TStringDynArray, and many others.

DynamicArray<T>::iterator

DynamicArray<T>::iterator is defined as an internal type of DynamicArray<T>; therefore, it implements begin() and end() as member functions. The rest of the supported iterators do not have this feature because they are wrappers over Delphi classes with auto-generated headers; this means you can use this iterator with BCC.

System::DynamicArray<int> a(10,20,20,30);
System::DynamicArray<int>::iterator iter = a.begin();
while (iter != a.end()) {
  std::cout << *iter << std::endl;
  ++iter;
}

The Clang-enhanced compilers allow you to use range-for syntax:

System::DynamicArray<int> a({10,20,30,40,50});
for(int value: a) {
  std::cout << value << std::endl;
}

The key feature of this iterator is that it allows you to access the elements by reference; this means you can modify the contents of a DynamicArray using an iterator and access the full power of STL algorithms.

/* This snippet uses STL Algorithms, remember to add '#include <algorithm>' at the beginning of your file  */
 
System::DynamicArray<int> a({10,20,30,40,50});
 
System::DynamicArray<int>::iterator iter = a.begin();
iter[0] = -30;
  
// Lambdas support is Clang-only 
// We can use functors in BCC
std::for_each(a.begin(),a.end(),[](int value){
  std::cout << value << std::endl;
});
  
std::random_shuffle(a.begin(),a.end());
std::sort(a.begin(),a.end());
Note: If RTTI is disabled in BCC (via IDE compiler options or #pragma), BCC generates E2366. You can compile the file by disabling iterator support. Add this code before any #include directive in the .cpp file that triggers the error.
#define NO_SYSDYN_ITERATOR

TRandomIterator::<T,E>

TRandomIterator::<T,E> is defined for every RTL container that implements the integer subscript operator E operator[ ](int) and an integer Count property. The key difference with DynamicArray iterators is that you can only access items by reference if E is a reference type. Most auto-generated .hpp headers define operator[] with a value return type like TList,TStringList or TStrings; this also means you cannot use operator->() on an iterator to access fields or use modifying STL algorithms like std::sort.

Since this is an iterator wrapper defined outside the container, you have to use non-member begin() and end() to enable it:

TList *foo = new TList();
auto it = begin(foo);

The actual implementation of begin() and end() is defined using ADL and SFINAE. BCC is not currently supported. Check the sysiterator.h header for more details.

TEnumerableIterator<T>

TEnumerableIterator::<T> is defined for every Delphi RTL container that implements the GetEnumerator() method. The returned Enumerator Type must also support the GetCurrent() and MoveNext() methods. This pattern is frequently used in RTL code, so it covers a good portion of containers. TEnumerableiterator and TRandomITerator are Input Iterators; which means they are read-only and forward-only, and are implemented using non-member begin() and end() so they are only enabled with Clang-based compilers. The supported features are:

  • Range-for
  • Non-modifying STL algorithms that operate on input iterators like:

One key lacking operator in TEnumerator is the iterator difference (std::distance); therefore, some algorithms that rely on iterator distance such as std::binary_search or std::count are not available.

As part of the collection enumerator types, RAD Studio formally defines the enumerator status when the iteration has completed: “The enumerator state is not valid after MoveNext returned False and enumerator must be released or recreated and should not be accessed any further”.

Insertion Iterators

Although you cannot modify some containers in place, you can insert or remove new elements using the concept of Insertion Iterators to make copies from STL iterators to RTL Containers and vice-versa. Use the following custom functions to create those iterators:

  • System::back_inserter<T>(T *container)
  • System::front_inserter<T>(T *container)
  • System::make_inserter<T>(T *container, size_t place)
// This code uses STL Algorithms
// Remember to '#include <algorithm>'
 
std::vector<UnicodeString> v{"A","B","C","D","E"};
TStringList *list = new TStringList;
TStringList *list2 = new TStringList;
  
std::copy(v.begin(),v.end(),back_inserter(list)); // list == A, B, C, D, E
std::copy(v.begin(),v.end(),front_inserter(list2)); // list2 == E, D, C, B, A
std::copy(v.begin(),v.end(),make_inserter(list2,1)); // list2 == E, E, D, C,B, A, D, C, B, A
  
std::vector<UnicodeString> filtered;
// Copy only items from list2 that equal "C"
std::copy_if(begin(list2),end(list2),std::back_inserter(filtered),[](const UnicodeString &str){
  return "C" == str;   
});
// filtered == C,C

See Also