четверг, 28 марта 2013 г.

Generic'и без Generic'ов....


Когда-то Акжан Абдулин это уже писал... Делюсь... Я лет 8-мь это уже 
использую...

шаблоны в Delphi 7:

TList.intf:
TList = class(Parent)
Add(a: ItemType);
Insert(i: Integer; a: ItemType);
Sort();
end;

..

TList.impl
TList.Sort()
..
CompareItems(a, b) : Integer;

..
Parent = TObject;
ItemType = integer;

{$include TList.intf}

TIntList = class(TList)
..

CompareItems(a, b : Integer) : Integer;
begin
Result := a - b;
end;

{$Include TList.impl} 
-------------------------------------------------------------------

Пример:


------------------------------------------------------------------------------------------------
ИТОГО:

stack.imp.pas:

{$IfNDef Stack_imp}
 
{$Define Stack_imp}
 ItemsHolder = array of _ItemType_;
 
 _Stack_ = {mixin} class(TObject)
 private
 // private fields
   f_Items : ItemsHolder;
 public
 // public methods
   procedure Push(const anItem: _ItemType_);
   function Pop: _ItemType_;
 end;//_Stack_
 
{$Else Stack_imp}
 
// start class _Stack_
 
procedure _Stack_.Push(const anItem: _ItemType_);
var
 l_L : Integer;
begin
 l_L :=  Length(f_Items);
 SetLength(f_Items, l_L + 1);
 f_Items[l_L] := anItem;
end;//_Stack_.Push
 
function _Stack_.Pop: _ItemType_;
var
 l_L : Integer;
begin
 l_L :=  Length(f_Items) - 1;
 Result := f_Items[l_L];
 SetLength(f_Items, l_L);
end;//_Stack_.Pop
 
{$EndIf Stack_imp}

----------------------------------------------------------------------------------------------
IntStack.pas:

unit IntStack;
 
interface
 
type
 _ItemType_ = Integer;
 {$Include Stack.imp.pas}
 TIntStack = class(_Stack_)
 end;//TIntStack
 
implementation
 
{$Include Stack.imp.pas}
 
end.

----------------------------------------------------------------------------------------------
StringStack.pas:

unit StringStack;
 
interface
 
type
 _ItemType_ = AnsiString;
 {$Include Stack.imp.pas}
 TStringStack = class(_Stack_)
 end;//TStringStack
 
implementation
 
{$Include Stack.imp.pas}
 
end.


!!!! Понятно, что ДИНАМИЧЕСКИЙ МАССИВ тут используется просто для примера. Понятно, что было можно ввести PItemType = ^_ItemType_ и оперировать указателями и GetMem. Но это усложнило бы пример.
И понятно, что тут нету проверки граничных условий.

Попробуйте. И может быть - вам понравится.

Ну а про настоящие generic'и читаем тут - http://keeper89.blogspot.ru/2011/07/delphi.html

А одна из моих реализаций данной идеи описана тут - http://18delphi.blogspot.com/2013/07/2_18.html

15 комментариев:

  1. Ничего не понял, а можно исходник с примером?

    ОтветитьУдалить
  2. Так это исходник и есть :-)

    один файл:
    TList.intf:
    TList = class(Parent)
    Add(a: ItemType);
    Insert(i: Integer; a: ItemType);
    Sort();
    end;

    второй файл:
    TList.impl:

    TList.Sort()
    - тут реализуем Sort используя CompareItems (которую определим потом)

    TList.Insert(i: Integer; a: ItemType);
    - тут реализуем Insert используя ItemType, который определим потом

    третий файл:

    IntList.pas:

    interface

    Parent = TObject;
    ItemType = integer;
    // - определили ItemType

    {$include TList.intf}
    // - включили интерфейс списка

    TIntList = class(TList)
    ..

    implementation

    CompareItems(a, b : Integer) : Integer;
    // - определили функцию сравнения
    begin
    Result := a - b;
    end;

    {$Include TList.impl}
    // - включили реализацию списка

    ОтветитьУдалить
  3. Завтра продолжу немножко занудствовать про TDD и нарисую два DUnit-теста к получившимся классам.

    А также немножко позанудствую про примеси и нарисую как сделать не только TIntStack, Но и TPesistentIntStack и TComponentedIntStack.

    ОтветитьУдалить
  4. Инетересно, но вот форматера кода явно не хватает/ Да и почему то комнтарий лучше воспринимается чем сама статья. Буду ждать продолжения.

    ОтветитьУдалить
  5. компилируемый пример,я думал что это будет архивчик с проектом, а так он не компилируемый вовсе :)

    ОтветитьУдалить
  6. И в каком месте он не компилируемый?

    ОтветитьУдалить
  7. Я прямо в восторге, век живи век учись.
    А теперь компилируемый, раньше ссылка была на файл *.impl.pas, а сам файл создавался просто *.impl и была куча странных коментов которые взрывали мозг, теперь лучше. Спасибо за пост.

    ОтветитьУдалить
  8. Присоединяюсь по поводу восторгов, это я писал первый комментарий, теперь все понятно, спасибо.

    З.Ы. Не компилироваться может потому что inc файл не нужно включать в проект. Лучше было бы его вообще сделать с другим расширением, чтоб не путаться.

    ОтветитьУдалить
  9. Про расширение - согласен. Но тогда подсветка синтаксиса по-умолчанию не работает. Надо настраивать.

    Файлы *.imp.pas - действительно НЕ НАДО включать в проект.

    По поводу восторгов - ложка дёгтя. В "шаблонах" breakpoint'ы не ставятся. Оно в общем и понятно - почему. Потому, что компилятор делает несколько копий кода. А отладчик не настолько умён - чтобы с ними разобраться. Но есть техники, которые позволяют это обойти. Позже - расскажу как.

    ОтветитьУдалить
  10. Вот продолжение - http://18delphi.blogspot.com/2013/03/blog-post_29.html

    ОтветитьУдалить
  11. кстати в Delphi 2010 брекпоинты очень даже хорошо ставятся и прекрасно работают :)

    ОтветитьУдалить
    Ответы
    1. Везёт...
      Ну тут варианта - два. Либо они это доделали. Во что мне лично, при всём моём уважении, не очень вериться.
      Либо у тебя примесь примешивается (инстанцируется) РОВНО ОДИН раз. В это мне вериться - больше.

      Удалить
    2. может дело в расширении файла? inc vs pas?
      я проверял на примере UTF8LengthLimiter - inc инстанцируется два раза; одна точка останова отрабатывает сразу для обоих инстанций - вот это не очень удобно..

      Удалить