Показаны сообщения с ярлыком tips'n'tricks. Показать все сообщения
Показаны сообщения с ярлыком tips'n'tricks. Показать все сообщения

воскресенье, 15 сентября 2013 г.

Коротенько. SizeOf в $IfDef

Оказывается не все знают.

В $If например можно написать так:

{$If SizeOf(ItemType) > 4}

Удобно. Для примесей и абстрактных контейнеров.

четверг, 20 июня 2013 г.

Опять займусь самоцитированием. Из прошлого...

Из совсем прошлого:

"ещё до "открытия" для себя IUnknown я придумал "фабрики интерфейсов (инструментов)".. на базовом объекте я ввёл метод QueryTool, который возвращал по идентификатору "инструмента" либо новый объект, если текущий не был совместим с указанной VMT, либо текущий, если он был совместим с указанной VMT.. Но тут понадобился подсчёт ссылок (http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%81%D1%81%D1%8B%D0%BB%D0%BE%D0%BA)... и он - БЫЛ РЕАЛИЗОВАН... так вот.. а потом - я "открыл" для себя IUnknown, когда Delphi 3 вышли..."

Но в Delphi 3 - Не было TInterfacedObject (или я не знал про него). Посему - "мой подсчёт ссылок" - остался в неизменном виде. Он - СИЛЬНО отличается от TInterfacedObject. Хотя бы на +1 в конструкторе. И он дожил ДО СИХ ПОР. А ТЕПЕРЬ - он мне сильно нравится. ГОРАЗДО больше, чем TInterfacedObject. Он - СИММЕТРИЧНЕЕ. И позволяет "смешивать объекты с интерфейсами". Безболезненно. И не задумываться о const или не const.

+1 в конструкторе и "фабричные методы". Одна из разниц. Но какая!

Вот ссылка кстати - http://18delphi.blogspot.com/2013/04/iunknown.html

Ну и "ДО" IUnknown я сильно "баловался" процедурами с директивой message... Работало - как надо... Только - медленно.

От message я не так давно избавился... Лет пять назад...

А от QueryTool - так и не избавился. Они мне нравятся. Но только перевёл их на IUnknown.

понедельник, 3 июня 2013 г.

Offtopic. Человек написал...

"Не могли бы Вы мне помочь с компонентами ExprDraw и ExprMake?"

Я ответил - "с удовольствием. Чем именно помочь?"

И тишина... Наверное - помог :-) Поддержал так сказать морально...

P.S. Тем кто спрашивает "как встроить формулу в MEMO-поле" - я бы посоветовал бы почитать GoF ("Банду четырёх"), хотя бы главу про "глифы". Формула (или картинка) и есть - "глиф". Подумайте об этом. На том месте, где можно вывести "символ" - можно вывести и "глиф". Достаточно ПЕРЕОПРЕДЕЛИТЬ высоту/ширину "глифа", а также функцию отрисовки. И - дело в шляпе. Ещё можно посмотреть на xCode и поискать в интернете - kCTRunDelegateAttributeName . Например - http://www.pvsm.ru/ios-development/29392.

P.P.S. http://ru.wikipedia.org/wiki/%D0%93%D0%BB%D0%B8%D1%84

P.P.P.S И ещё раз - http://18delphi.blogspot.com/2013/04/blog-post_10.html и http://18delphi.blogspot.com/2013/04/blog-post_8517.html

P.P.P.S. Т.е. если стоит задача "вывести формулу (или картинку) в MEMO или RichEdit", то вы просто вешаете "обработчик" на отрисовку (и измерение ширины/высоты) КАЖДОГО символа, и дальше в этом обработчике - делаете всё, что вам заблагорассудится. Как то так.. Дальше нужен пример?

P.P.P.P.S. Ещё раз - Картинка (или формула) и есть - "символ", т.е. - "глиф". КАЖДЫЙ "глиф" имеет свои метрики (ширину и высоту) и функцию отрисовки. Думайте так, как будто "кто-то" рисует за вас этот "глиф" (и измеряет его). А если хотите чего-то сложного типа формулы или картинки - поймите, что именно вам эти функции и надо обеспечить.

P.P.P.P.P.S. "Рецепта" на ВСЕ СЛУЧАИ жизни - я вам не дам. Разве, что только устно. Да и то - не факт. Можно выложить "супер-компонент", рисующий и текст и картинки и формулы, но там опять же - будут вопросы. Много вопросов. Проще "дать удочку", нежели "накормить рыбой".

суббота, 11 мая 2013 г.

Банальное об отладке и трассировке (5)

Если программа падает не дойдя до первого begin из program, то скорее всего проблема либо в отсутствующих DLL, либо что-то случается в одной из секций инициализации.

Проблему с DLL можно определить через showdep.

Проблемы в initialization - можно логировать.

Например простым скриптом обработать исходные файлы:


USES
 WordsTuning.script
;
 
: InsertLog
 
 WORDWORKER DoFiles
 
  : DoDirPrim STRING IN aDir
   WordToWork '*.pas' aDir ProcessFilesWithMask
  ; // DoDirPrim
 
  FORWARD DoDir
 
  : DoDir STRING IN aDir
   aDir .
   aDir DoDirPrim
   @ DoDir aDir ProcessSubDirs
  ; // DoDir
 
  : DoRoot STRING IN aRoot
    @ DoDir aRoot ProcessSubDirs
    aRoot DoDirPrim
  ; // DoRoot
 
  //'W:common\components\rtl\external\BorlandDelphi\Vcl' DoRoot
  'W:common\components\rtl\external\BorlandDelphi' DoRoot
  'W:common\components\gui' DoRoot
  'W:common\components\rtl\Garant' DoRoot
 ; // DoFiles
 
 : TestFile STRING IN aIn
  STRING VAR cOut 
  aIn '.log' Cat =: cOut
  cOut .
 
  BOOLEAN VAR l_Changed
  false =: l_Changed
 
  BOOLEAN VAR l_WasEnter
  false =: l_WasEnter
 
  FILE VAR l_In
  aIn file:OpenRead =: l_In
  TRY
   FILE VAR l_Out
   cOut file:OpenWrite =: l_Out
   TRY
 
   : DoLine W-STRING IN aStr
 
    CONST cTouched '{!touched!}'
 
    ( l_WasEnter И ( 
      ( aStr РАВНО 'end.' ) ИЛИ
      ( aStr РАВНО 'finalization' )
      ) 
    ) ? (
      cTouched l_Out file:WriteStr
      [[ '{$IfDef LogInit} ' 'WriteLn(''' aIn ' initialization leave'');' ' {$EndIf}' ]] strings:Cat l_Out file:WriteStr
      #13#10 l_Out file:WriteStr
      false =: l_WasEnter
    )
 
    ( aStr "начинается с" cTouched ) ! IF
     aStr l_Out file:WriteWStrLn
     ( aStr РАВНО 'initialization' ) ? (
      true =: l_Changed
      true =: l_WasEnter
      cTouched l_Out file:WriteStr
      [[ '{$IfDef LogInit} ' 'WriteLn(''' aIn ' initialization enter'');' ' {$EndIf}' ]] strings:Cat l_Out file:WriteStr
      #13#10 l_Out file:WriteStr
     )
    ELSE 
     true =: l_Changed
    ENDIF
   ;
 
   l_In file:ReadLines DoLine
 
   FINALLY
    nil =: l_Out
   END 
  FINALLY 
   nil =: l_In
  END 
 
  l_Changed IF
   aIn DeleteFile ! ? (
    [[ 'Не удалось удалить ' aIn ]] strings:Cat .
   )
 
   cOut aIn RenameFile ! ? (
    [[ 'Не удалось переименовать ' cOut ' в ' aIn ]] strings:Cat .
   )
  ELSE
   cOut DeleteFile ! ? (
    [[ 'Не удалось удалить ' cOut ]] strings:Cat .
   )
  ENDIF
 
  //cOut '%' tests:CheckEtalon
 ; // TestFile
 
 DoFiles TestFile
;
 
InsertLog

Банальное об отладке и трассировке (4)

Если программа без отладчика вылетает внезапно в каком-то пользовательском сценарии без всяких признаков ошибки, то ОЧЕНЬ велика вероятность, что мы вошли в бесконечную рекурсию и случился Stack Overflow.

Так вот. Если программу запустить под отладчиком и повторить данный сценарий. То велика вероятность, что и среда Delphi - "зависнет".

Так вот. Тут стоит проявить ТЕРПЕНИЕ. И подождать минут 10-ть, а то и 20-ть.

И ОЧЕНЬ велика вероятность, что среда "отвиснет" и выведет сообщение об Exception "Stack Overfow". И ГЛАВНОЕ - ПОКАЖЕТ нам СТЕК. И наше терпение - будет вознаграждено.

И мы УВИДИМ по стеку место бесконечной рекурсии. И сможем его поправить.

Опять же - почему пишу? Да потому что многие об этом просто не догадываются... Ну или не имеют терпения.

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

Банальное об отладке и трассировке (3)

Иногда отладчик пишет в Watches нечто подобное:

l_LocalVar : ISomeInterface - TSomeInterfaceImplementor at $abcd123

Так вот. Если мы ожидаем, что этот ISomeInterface реализуется классом TSomeInterfaceImplementor, то если написать TSomeInterfaceImplementor($abcd123).SomeObjectProp, то мы можем получить состояние РЕАЛИЗУЮЩЕГО объекта, а не интерфейса. На интерфейсе многие методы и/или свойства - могут быть недоступны.

Но через ОБЪЕКТ реализации мы их можем увидеть в процессе отладки. По-моему - очень удобно.

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

Типа "венгерская нотация". Ещё немножко банальщины

Глобальные переменные я пишу - g_GlobalVar.
Локальные - l_LocalVar или теперь - vLocalVar (v - от слова var(iable)).
Входные параметры - a(n)InParam.
Выходные параметры - theOutParam.
Члены класса - f_Field.
Локальные константы - cConst.
Вложенные локальные процедуры часто пишу - lpLocalProc.

По-моему - очень удобно.

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

Как я оформляю методы доступа к свойствам объекта

Я пишу не так:

TA = class
 protected
  function GetValue: Integer; virtual;
  procedure SetValue(aValue: Integer); virtual;
 protected
  property Value: Integer
   read GetValue
   write SetValue;
end;


А вот так:

TA = class
 protected
  function pm_GetValue: Integer; virtual;
  procedure pm_SetValue(aValue: Integer); virtual;
 protected
  property Value: Integer
   read pm_GetValue
   write pm_SetValue;
end;


-- почему? Да потому, чтобы исключить прямой вызов методов доступа к свойству. GetValue - он не "режет глаз", а pm_GetValue - РЕЖЕТ. Есть что-то неестественное в этой записи. И эта неестественность - коррелирует с неестественностью НЕПОСРЕДСТВЕННОГО вызова метода доступа к свойству.

Ну или использую - private, если виртуальность не нужна. Но всё равно стараюсь писать pm_GetX/pm_SetX.

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

Банальное об отладке и трассировке (2)

Может быть кто-то не знает, но в функции отладчика Evaluate Value (Ctrl-F4) - можно не только ПОСМОТРЕТЬ значение переменной, но и ИЗМЕНИТЬ его. Самая нижняя строка - New Value. Вводим там новое значение переменной и жмём Enter. Иногда - это КРАЙНЕ удобно. Например для отладки редких граничных случаев.

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

Опять же - почему пишу о такой банальщине? Да потому, что я видел ОЧЕНЬ много людей, которые этим либо не пользуются, либо забывают об этом.

Банальное об отладке и трассировке

Пусть мы оказались с каком то месте трассировки.

Как узнать метод какого РЕАЛЬНО класса мы трассируем?

Всё до банального просто - пишем в Watches - ClassName и ставим галку "Allow function call" (или "Allow side effects" в XE).

И ВИДИМ - имя реального класса, а не того, который указан в заголовке метода.

Всё просто, как пять копеек.

Почему я пишу об этом? Да потому, что я за свою практику видел наверное ТРИ ДЕСЯТКА (а то и больше) людей, который этим либо не пользуются, либо - забывают о такой возможности.

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

Вызов методов предка с ключевым словом inherited

Delphi позволяет нам использовать ДВЕ формы вызова родительского метода при помощи ключевого слова inherited.

С использованием имени метода:

procedure TSomeObject.Cleanup; // override
begin
 SomeOwnCode;
 inherited Cleanup;
end;


И без использования имени метода:

procedure TSomeObject.Cleanup; // override
begin
 SomeOwnCode;
 inherited;
end;


-- я лично при прочих равных - предпочитаю ВТОРУЮ форму.

Почему? Потому, что при активном рефакторинге и переименовании методов - она - УСТОЙЧИВЕЕ. Не надо бегать и синхронизировать имена методов.

Есть правда одна ЗАСАДА.

Если ВДРУГ написано так:

type
 TA = class
  protected
   procedure Cleanup; virtual;
 end;
 
 TB = class(TA)
  private
   procedure Cleanup; override;
 end;
 
 TC = class(TB)
  protected
   procedure Cleanup; override;
 end;


-- то при записи:

procedure TC.Cleanup; //override;
begin
 SomeOwnCode;
 inherited;
end;


Вызовется не тот метод, который мы ожидаем - TB.Cleanup, а метод TA.Cleanup. Хм.. (задумался)... Или НИЧЕГО не вызовет. Не помню. Склероз. Завтра - проверю.

Правда компилятор выдаст Warning о понижении видимости виртуального метода.

А при вызове:

procedure TC.Cleanup; //override;
begin
 SomeOwnCode;
 inherited Cleanup;
end;


-- нас обругает компилятор. Скажет - "не знаю такого метода в базовом классе".

понедельник, 22 апреля 2013 г.

Как правильно сравнивать ссылки на интерфейсы

Чтобы корректно узнать - являются ли интерфейсы одним и тем же объектом надо действовать примерно так:


function l3IEQ(const I1, I2: IUnknown): Boolean;
  {-}
var
 iu1 : IUnknown;
 iu2 : IUnknown;
begin
 Result := (I1 = I2);
 if not Result AND (I1 <> nil) AND (I2 <> nil) then
 begin
  if Succeeded(I1.QueryInterface(IUnknown, iu1)) then
   try
    if Succeeded(I2.QueryInterface(IUnknown, iu2)) then
     try
      Result := (iu1 = iu2);
     finally
      iu2 := nil;
     end;//try..finally
   finally
    iu1 := nil;
   end;//try..finally
 end;//not Result..
end;


... тут будут тесты ....

воскресенье, 21 апреля 2013 г.

Фабричный метод

Вот тут был достаточно развесистый пост про подсчёт ссылок - http://18delphi.blogspot.com/2013/04/iunknown.html

Хочу обратить внимание на следующий кусок:

 TmyReferenceCountGuard = class(TmyInterfacedObject, ImyReferenceCountGuard)
  {* Класс исключительно для примеров }
...
public
 // public methods
   class function Make: ImyReferenceCountGuard; reintroduce;
     {* Фабрика TmyReferenceCountGuard.Make }
 end;//TmyReferenceCountGuard
....
 
class function TmyReferenceCountGuard.Make: ImyReferenceCountGuard;
var
 l_Inst : TmyReferenceCountGuard;
begin
 l_Inst := Create;
 try
  Result := l_Inst;
 finally
  l_Inst.Free;
 end;//try..finally
end;


И его использование:
procedure TmyReferenceCountGuardTest.DoIt;
var
 l_G : ImyReferenceCountGuard;
begin
 l_G := TmyReferenceCountGuard.Make;
...


Вне зависимости от того как реализован подсчёт ссылок - я предпочитаю пользоваться ИМЕННО фабричным методом, а не конструктором. Во-первых - его использование подчёркивает тот факт, что конструируется ИМЕННО интерфейс, а не просто объект. А во-вторых - снимается опасность использования конструктора при передаче интерфейса внутрь метода с параметром const (то что описал gunsmoker тут - http://www.gunsmoker.ru/2013/04/plugins-9.html (Неочевидная особенность учёта ссылок интерфейсов (конструктор в const-параметре))).

Да и читать мне такое приятнее/проще. А при "моём подсчёте ссылок" - ещё и скрывается лишний Free (внутрь фабричного метода).

Ну и понятное дело, что можно сделать несколько фабричных методов - для создания одним классом разных интерфейсов. И например с overload по параметрам.

Да и кеширование в фабричный метод всегда можно потом безболезненно воткнуть, или логирование, ну или бизнес-логику какую. Или например граничные условия обработать.

По мне - если бы это позволял корректно сделать компилятор - я бы вообще конструктор бы скрыл в данном конкретном случае. А так - "приходится договариваться с коллегами".

Следующим постом я надеюсь - доберусь до подсчёта ссылок и контейнеров.

(!)(!)(!) ДА в случае наследования от TInterfacedObject фабричный метод выглядит так:

class function TmyReferenceCountGuard.Make: ImyReferenceCountGuard;
begin
 Result := Create;
end;

А знаете ли вы, что dpk можно отлаживать и трассировать?

А знаете ли вы, что dpk можно отлаживать и трассировать? Второй копией Delphi. Через Run -> Parameters. Указываем там путь к delphi.exe.

Очень удобно при разработке собственных компонент. Когда они вдруг падают в Design-Time.

Просто спросил.

Кстати оказалось, что знают далеко не все...

четверг, 18 апреля 2013 г.

Два монитора для отладки GUI

Два монитора для отладки GUI - это чертовски удобно. И не сильно критична их диагональ. Главное, чтобы их было ДВА.

А особенно для отладки отрисовки собственных контролов. На одном мониторе отладчик - на другом - отлаживаемое приложение.

Можно ставить break-point и не попадать на него бесконечно много.

Учитываю, что "двухголовые" видео-карты сейчас скорее стандарт, чем редкость, А мониторы стоят почти "копейки" - это реально удобно использовать.

Звучит банально. Да.

Но попробуйте если ещё не пробовали. Может быть вам понравится.


P.S. у меня правда и компьютеров-то - пять штук - два Мака и три PC. Но это - того стоит...

"Американский" метод отладки №2

Пусть программа падает на строчке:


DoSomething(A, B);


И при этом падает когда A = 'X', а B=10.

Можно конечно ставить Conditional Break-Point.

Но проще написать:


if (A = 'X') AND (B = 10) then
 DoSomething(A, B) // - сюда ставим break-point
else
 DoSomething(A, B);


банально.. да...

Но попробуйте. Может быть вам понравится.

Съэкономите время и нервы..

среда, 17 апреля 2013 г.

Ещё для затравки...

Чем плоха конструкция:


ImyInterface = interface(IUnknown)
end;//ImyInterface
 
TA = class(TInterfacedObject, ImyInterface)
end;//TA
 
TB = class(TA, ImyInterface)
end;//TB


-- расскажу позже.

В двух словах - можно получить от ОДНОГО объекта две ссылки на ImyInterface, которые будут вести себя по-разному.

Например так:


function TA.SomeMethod: ImyInterface;
begin
 Result := Self;
end;
 
...
var
 l_X : TB;
 l_A : ImyInterface;
 l_B : ImyInterface;
begin
 l_X := TB.Create;
 l_A := l_X.SomeMethod;
 l_B := l_X As ImyInterface;
 Assert(l_A = l_B, 'Улыбайтесь пошире и удивляйтесь :-)');
end;


или так:




var
 l_X : TA;
 l_A : ImyInterface;
 l_B : ImyInterface;
begin
 l_X := TB.Create;
 l_A := l_X;
 l_B := l_X As ImyInterface;
 Assert(l_A = l_B, 'Улыбайтесь пошире и удивляйтесь :-)');
end;

пятница, 12 апреля 2013 г.

"Американский" метод отладки

Пусть программа падает на строчке:


DoSomething;


И падает далеко не с первого раза. Но стабильно. Как её отладить?

Всё очень просто:

Пишем:

try
 DoSomething;
except
 DoSomething; // - тут ставим бреак-поинт
end;


Когда вывалились на break-point - можно посмотреть интересующие переменные, регистры и стек. А ещё можно попробовать войти внутрь DoSomething и протрассировать её по шагам. Есть достаточно большая вероятность того, что второй прогон будет сильно похож на первый. И ошибка будет найдена.

Работает конечно не всегда. Но очень часто - работает.

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