четверг, 19 марта 2009 г.

Указатель на метод интерфейса

Понадобилось на днях коллегам передать в качестве callback-функции метод объекта, доступ к которому есть только через интерфейс. Показалось, что проблемы особой нет - интерфейс "знает" о своем объекте, осталось вычислить адрес объекта да самого метода.

Но реальность оказалась не столь оптимистичной - на текущий момент Delphi такого функционала не поддерживает:
  • http://groups.google.com.ua/group/borland.public.delphi.objectpascal/browse_thread/thread/4c5dcb094b82522f?pli=1
  • http://qc.embarcadero.com/wc/qcmain.aspx?d=941 (а судя по датам и не будет поддерживать)
Самому раньше в таком функционале необходимости не было - проще передавать весь интерфейс, но если необходимо взаимодействие с чужим кодом, где могут требовать именно указатель на функцию, то прийдется изобретать не очень стройные решения:
  • метод в интерфейсе напрямую возвращающий адрес метода объекта
IAnIntf = interface
function GetPointerToProc: TAnObjectProc:
end
  • объект-обертку над интерфейсом
TWrapObj = class
private
AnIntf: ITheIntf;
protected
procedure AnProc(const Value: string);
end;

procedure TWrapObj.AnProc(const Value: string);
begin
AnIntf.AnProc(Value);
end;
  • возможно как-то можно будет выкрутиться через анонимные функции? нельзя, ибо
    TAnProc = procedure (S: string) of object;
    TAnProc2 = reference to procedure (S: string);
    это разные типы
Хотя и в реализации проблем более чем - вычислять статический адрес, на этапе компиляции, для такого указателя не получится, т.к. один и тот же интерфейс может реализовываться разными объектами.

> Still I think making (procedure of object) and (procedure of Interface) equivalent
> typeswould be the better solution.

Like I said, the semantics for the compiler would be quite different, so
it would make sense to make them different types (even if both
represented internally by a TMethod). Actually, if the interface
instance variable is nil, it is impossible to get the method address.
This is not a problem for objects, since there the static address is
taken.

пятница, 6 февраля 2009 г.

Создание индексов "на ходу" в Firebird

>> А вот с индексами как? Как насчет CREATE INDEX "на ходу"? Да еще и на
>> таблицу, с которой постоянная работа на UPDATE идет?
>
> Если 2.0 и старее, то получишь рассинхронизированный с таблицей индекс.
> Если 2.1 и новее, то все апдейты будут курить, пока индекс не будет создан.

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

> и создание индексов, и ALITER INDEX INACTIVE делал на ходу...
> и что требуется теперь сделать, что бы за БД не переживать?

gfix -v -f

(с) Dmitry Yemanov, Хорсун Влад

пятница, 9 января 2009 г.

Borland, умолчания и поддержка старого

Компания меняет не только названия, но и простым разработчикам не дает скучать.
Вводная - при неизменных внешних условиях перестала выполняться сборка релиза из командной строки при переходе с 2007 на 2009 - упорно попадал туда код дебага. В то время как из среды - все ок.

Проведенное в два этапа(первый был еще на 2007 и текущей проблемы не касался см.) расследование показало, что:

- в импортируемом проектами файле CodeGear.Delphi.Targets дефолтный таргет изменен с Build на Make. А последний при смене конфигурации Debug|Release не пересобирает dcu

- группы проектов, в отличии от проектов, не обновляются до соотв. версии и импортируют $(MSBuildBinPath)\Borland.Group.Targets, который существует только при инсталяции 2007(или когда там msbuild прикрутили?)

- таргет Build для группы проектов не задает таргет для конечных проектов(т.е. используется умолчательный, который теперь Make, да)

Выходом может быть использование таргета BuildAll для группы проектов.

Delphi 2009 и приведение к PAnsiString

Часть winapi функций требуют передачи себе в кач-ве параметров структур с PAnsiChar строками (например mapi). В предыдущих версиях приведение строк посредством PChar(SomeString) работало, используя область памяти выделенную под SomeString, а в 2009 результат PAnsiString(AnsiString(SomeString)), уже как вычисление обычной строки из юникодной, размещается в локальной области памяти, что чревато тем, что старый код может перестать работать на, казалось бы, ровном месте (хотя конечно прямое приведение к pchar тоже имеет кучу особенностей).

Например код приведенный ниже возвращает разные результаты на 2007 и 2009 Делфи

program array_error_test;

{$APPTYPE CONSOLE}

uses
SysUtils,
StrUtils,
Classes;

procedure DoMain;
const
Count = 5;

type
RecType = record
AnString: PAnsiChar;
end;
var
RecArr: array of RecType;
I: Integer;
SomeStrings: TStringList;
begin

SetLength(RecArr, Count);

SomeStrings := TStringList.Create;
try
for I := 0 to Length(RecArr) - 1 do
begin
SomeStrings.Add(DupeString(IntToStr(I), 10));
RecArr[I].AnString := PAnsiChar(AnsiString(SomeStrings[I]));
end;

for I := 0 to Length(RecArr) - 1 do
WriteLn(RecArr[I].AnString);
finally
SomeStrings.Free;
end;
end;

begin
DoMain;
ReadLn;
end.

четверг, 8 января 2009 г.

Delphi 2009 и InternetQueryOption

Переносится в одном из проектов код на 2009 Делфи и соотвественно кушаются все прелести первопроходчества.

Для получения адреса прокси сервера пользовалась функция InternetQueryOption. После сборки в 2009 стала она возвращать мусор. Ничего криминального замечено не было, замена на InternetQueryOptionW не помогала.

Решение нашлось в
http://akirabbq.spaces.live.com/blog/cns!CEB8A04DC43BCEE9!264.entry
кто виноват не совсем понятно, но неприятно :(

По сути - в структуре INTERNET_PROXY_INFO lpszProxy должно быть не LPCTSTR(которое в 2009 уже PWideChar), а просто LPCSTR, хотя в исходных сишных заголовках она объявлена как LPCTSTR.

Решить проблему можно создав необходимую структуру у себя.

пятница, 12 сентября 2008 г.

Оптимизатор в Firebird 2.1

Прошло одно изменение без особого внимания, но оно может очень влиять на производительность при переходе со старых версий. В композитных индексах теперь считается селективность для каждого сегмента отдельно, что по логике правильнее, но в редких случая может менять порядок таблиц в соединении кардинально:

Selectivity Maintenance per Segment
D. Yemanov, A. Brinkman
Index selectivities are now stored on a per-segment basis. This means that, for a compound index on columns
(A, B, C), three selectivity values will be calculated, reflecting a full index match as well as all partial matches.
That is to say, the selectivity of the multi-segment index involves those of segment A alone (as it would be if it
were a single-segment index), segments A and B combined (as it would be if it were a double-segment index)
and the full three-segment match (A, B, C), i.e., all the ways a compound index can be used.
This opens more opportunities to the optimizer for clever access path decisions in cases involving partial index
matches.
The per-segment selectivity values are stored in the column RDB$STATISTICS of table RDB
$INDEX_SEGMENTS. The column of the same name in RDB$INDICES is kept for compatibility and still
represents the total index selectivity, that is used for a full index match.


и если была таблица documents(...doc_state int, doc_date ... ) c индексом по (doc_state, doc_date), то теперь, при условиях по doc_state, будет браться селективность не всего композита(а она могла из-за даты быть достаточно неплохой), а только поля doc_state.

четверг, 4 сентября 2008 г.

Microsoft runtime и Firebird Embeded 2.1.1

Для возможности x64 билдов разработчики Firebird перешли на vc8, что потянуло за собой, в принципе уже привычную, проблему распространения vc runtime.

Но учитывая теперешний зоопарк ОС, технологий совместимости 32/64, попыток решения dll хелл, и вообще постижения Дао от МС, деплоймент этого runtime становится очень непростым.

Он, рантайм 8-ой версии, не идет, пока, в поставки никакх ОС - и Висте его тоже нет(по меньшей мере до SP).

Первое - для правильной установки, как это рекомендует МС, нужны права Администратора. А для установки на win2k до SP1 - обновление инсталятора МС до 3.1

Для интеграции этого redist себе в инсталятор - нужно делать msi инсталяторы, создание которых в корректном виде не самое простое занятие, для котрого хороших
свободных инструментов вроде как и нет, и опять таки необходимы права администратора.

В принципе на первый вгляд проблем быть не должно - что стоит поставить этот рантайм,
когда разворачивается сервер СУБД. Но тем, кто пытается распространять shareware системы, особенно с FB embeded, приходится хуже - необходимо тянуть этот рантайм со своим приложением.

Есть три пути его поставки:
  1. Официальный.
    Устанавливаем инсталятором от MS и все работает. Не подходит, если нет гарантии наличия админских прав у пользователей, наличия Windows Installer 3.0 or later, portable решения.
  2. Экстенсивный.
    Поставляем со своим приложением просто необходимые бинарники рантайма. Так делает, впрочем, большинство софта - стоит только поискать msvcr80.dll по жесткому диску :)). Но, поставлять нужно ТРИ копии рантайма, одну для работы самого fbembed, вторую в каталоге intl для fbintl и третью в для подключаемых функций в каталоге udf.
  3. Хитро-экстенсивный.
    Пользуясь текстовой природой манифестов, в каталогах intl и udf оставить только манифест, с измененными путями к файлам вида:

    <file name="..\msvcr80.dll">
    <file name="..\msvcp80.dll">

    ВАЖНО, в таком случае указать в манифесте своего приложения зависимость от Microsoft.VC80.CRT.

    такой манифест представляет собой текстовый файл <FullAppName>.manifest с содержимым:
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
    <dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
    </dependency>
    </assembly>

    Этот манифест можно включить в ресурсы самого приложения, см. например


Ссылки: